/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"jsstr.h"#include"mozilla/Attributes.h"#include"mozilla/Casting.h"#include"mozilla/CheckedInt.h"#include"mozilla/FloatingPoint.h"#include"mozilla/PodOperations.h"#include"mozilla/Range.h"#include"mozilla/TypeTraits.h"#include"mozilla/Unused.h"#include<ctype.h>#include<limits>#include<string.h>#include"jsapi.h"#include"jsarray.h"#include"jsatom.h"#include"jsbool.h"#include"jscntxt.h"#include"jsgc.h"#include"jsnum.h"#include"jsobj.h"#include"jsopcode.h"#include"jstypes.h"#include"jsutil.h"#include"builtin/RegExp.h"#include"jit/InlinableNatives.h"#include"js/Conversions.h"#include"js/UniquePtr.h"#if ENABLE_INTL_API#include"unicode/uchar.h"#include"unicode/unorm2.h"#endif#include"vm/GlobalObject.h"#include"vm/Interpreter.h"#include"vm/Opcodes.h"#include"vm/Printer.h"#include"vm/RegExpObject.h"#include"vm/RegExpStatics.h"#include"vm/StringBuffer.h"#include"vm/Unicode.h"#include"vm/Interpreter-inl.h"#include"vm/String-inl.h"#include"vm/StringObject-inl.h"#include"vm/TypeInference-inl.h"usingnamespacejs;usingnamespacejs::gc;usingJS::Symbol;usingJS::SymbolCode;usingJS::ToInt32;usingJS::ToUint32;usingmozilla::AssertedCast;usingmozilla::CheckedInt;usingmozilla::IsNaN;usingmozilla::IsNegativeZero;usingmozilla::IsSame;usingmozilla::Move;usingmozilla::PodCopy;usingmozilla::PodEqual;usingmozilla::RangedPtr;usingJS::AutoCheckCannotGC;staticJSLinearString*ArgToRootedString(JSContext*cx,constCallArgs&args,unsignedargno){if(argno>=args.length())returncx->names().undefined;JSString*str=ToString<CanGC>(cx,args[argno]);if(!str)returnnullptr;args[argno].setString(str);returnstr->ensureLinear(cx);}/* * Forward declarations for URI encode/decode and helper routines */staticboolstr_decodeURI(JSContext*cx,unsignedargc,Value*vp);staticboolstr_decodeURI_Component(JSContext*cx,unsignedargc,Value*vp);staticboolstr_encodeURI(JSContext*cx,unsignedargc,Value*vp);staticboolstr_encodeURI_Component(JSContext*cx,unsignedargc,Value*vp);/* * Global string methods *//* ES5 B.2.1 */template<typenameCharT>staticLatin1Char*Escape(JSContext*cx,constCharT*chars,uint32_tlength,uint32_t*newLengthOut){staticconstuint8_tshouldPassThrough[128]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,1,1,1,/* !"#$%&'()*+,-./ */1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,/* 0123456789:;<=>? */1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,/* @ABCDEFGHIJKLMNO */1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,/* PQRSTUVWXYZ[\]^_ */0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,/* `abcdefghijklmno */1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,/* pqrstuvwxyz{\}~ DEL */};/* Take a first pass and see how big the result string will need to be. */uint32_tnewLength=length;for(size_ti=0;i<length;i++){char16_tch=chars[i];if(ch<128&&shouldPassThrough[ch])continue;/* The character will be encoded as %XX or %uXXXX. */newLength+=(ch<256)?2:5;/* * newlength is incremented by at most 5 on each iteration, so worst * case newlength == length * 6. This can't overflow. */static_assert(JSString::MAX_LENGTH<UINT32_MAX/6,"newlength must not overflow");}Latin1Char*newChars=cx->pod_malloc<Latin1Char>(newLength+1);if(!newChars)returnnullptr;staticconstchardigits[]="0123456789ABCDEF";size_ti,ni;for(i=0,ni=0;i<length;i++){char16_tch=chars[i];if(ch<128&&shouldPassThrough[ch]){newChars[ni++]=ch;}elseif(ch<256){newChars[ni++]='%';newChars[ni++]=digits[ch>>4];newChars[ni++]=digits[ch&0xF];}else{newChars[ni++]='%';newChars[ni++]='u';newChars[ni++]=digits[ch>>12];newChars[ni++]=digits[(ch&0xF00)>>8];newChars[ni++]=digits[(ch&0xF0)>>4];newChars[ni++]=digits[ch&0xF];}}MOZ_ASSERT(ni==newLength);newChars[newLength]=0;*newLengthOut=newLength;returnnewChars;}staticboolstr_escape(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);JSLinearString*str=ArgToRootedString(cx,args,0);if(!str)returnfalse;ScopedJSFreePtr<Latin1Char>newChars;uint32_tnewLength=0;// initialize to silence GCC warningif(str->hasLatin1Chars()){AutoCheckCannotGCnogc;newChars=Escape(cx,str->latin1Chars(nogc),str->length(),&newLength);}else{AutoCheckCannotGCnogc;newChars=Escape(cx,str->twoByteChars(nogc),str->length(),&newLength);}if(!newChars)returnfalse;JSString*res=NewString<CanGC>(cx,newChars.get(),newLength);if(!res)returnfalse;newChars.forget();args.rval().setString(res);returntrue;}template<typenameCharT>staticinlineboolUnhex4(constRangedPtr<constCharT>chars,char16_t*result){char16_ta=chars[0],b=chars[1],c=chars[2],d=chars[3];if(!(JS7_ISHEX(a)&&JS7_ISHEX(b)&&JS7_ISHEX(c)&&JS7_ISHEX(d)))returnfalse;*result=(((((JS7_UNHEX(a)<<4)+JS7_UNHEX(b))<<4)+JS7_UNHEX(c))<<4)+JS7_UNHEX(d);returntrue;}template<typenameCharT>staticinlineboolUnhex2(constRangedPtr<constCharT>chars,char16_t*result){char16_ta=chars[0],b=chars[1];if(!(JS7_ISHEX(a)&&JS7_ISHEX(b)))returnfalse;*result=(JS7_UNHEX(a)<<4)+JS7_UNHEX(b);returntrue;}template<typenameCharT>staticboolUnescape(StringBuffer&sb,constmozilla::Range<constCharT>chars){/* * NB: use signed integers for length/index to allow simple length * comparisons without unsigned-underflow hazards. */static_assert(JSString::MAX_LENGTH<=INT_MAX,"String length must fit in a signed integer");intlength=AssertedCast<int>(chars.length());/* * Note that the spec algorithm has been optimized to avoid building * a string in the case where no escapes are present. *//* Step 4. */intk=0;boolbuilding=false;/* Step 5. */while(k<length){/* Step 6. */char16_tc=chars[k];/* Step 7. */if(c!='%')gotostep_18;/* Step 8. */if(k>length-6)gotostep_14;/* Step 9. */if(chars[k+1]!='u')gotostep_14;#define ENSURE_BUILDING \ do { \ if (!building) { \ building = true; \ if (!sb.reserve(length)) \ return false; \ sb.infallibleAppend(chars.begin().get(), k); \ } \ } while(false);/* Step 10-13. */if(Unhex4(chars.begin()+k+2,&c)){ENSURE_BUILDING;k+=5;gotostep_18;}step_14:/* Step 14. */if(k>length-3)gotostep_18;/* Step 15-17. */if(Unhex2(chars.begin()+k+1,&c)){ENSURE_BUILDING;k+=2;}step_18:if(building&&!sb.append(c))returnfalse;/* Step 19. */k+=1;}returntrue;#undef ENSURE_BUILDING}/* ES5 B.2.2 */staticboolstr_unescape(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);/* Step 1. */RootedLinearStringstr(cx,ArgToRootedString(cx,args,0));if(!str)returnfalse;/* Step 3. */StringBuffersb(cx);if(str->hasTwoByteChars()&&!sb.ensureTwoByteChars())returnfalse;if(str->hasLatin1Chars()){AutoCheckCannotGCnogc;if(!Unescape(sb,str->latin1Range(nogc)))returnfalse;}else{AutoCheckCannotGCnogc;if(!Unescape(sb,str->twoByteRange(nogc)))returnfalse;}JSLinearString*result;if(!sb.empty()){result=sb.finishString();if(!result)returnfalse;}else{result=str;}args.rval().setString(result);returntrue;}#if JS_HAS_UNEVALstaticboolstr_uneval(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);JSString*str=ValueToSource(cx,args.get(0));if(!str)returnfalse;args.rval().setString(str);returntrue;}#endifstaticconstJSFunctionSpecstring_functions[]={JS_FN(js_escape_str,str_escape,1,JSPROP_RESOLVING),JS_FN(js_unescape_str,str_unescape,1,JSPROP_RESOLVING),#if JS_HAS_UNEVALJS_FN(js_uneval_str,str_uneval,1,JSPROP_RESOLVING),#endifJS_FN(js_decodeURI_str,str_decodeURI,1,JSPROP_RESOLVING),JS_FN(js_encodeURI_str,str_encodeURI,1,JSPROP_RESOLVING),JS_FN(js_decodeURIComponent_str,str_decodeURI_Component,1,JSPROP_RESOLVING),JS_FN(js_encodeURIComponent_str,str_encodeURI_Component,1,JSPROP_RESOLVING),JS_FS_END};staticconstunsignedSTRING_ELEMENT_ATTRS=JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT;staticboolstr_enumerate(JSContext*cx,HandleObjectobj){RootedStringstr(cx,obj->as<StringObject>().unbox());RootedValuevalue(cx);for(size_ti=0,length=str->length();i<length;i++){JSString*str1=NewDependentString(cx,str,i,1);if(!str1)returnfalse;value.setString(str1);if(!DefineElement(cx,obj,i,value,nullptr,nullptr,STRING_ELEMENT_ATTRS|JSPROP_RESOLVING)){returnfalse;}}returntrue;}staticboolstr_mayResolve(constJSAtomState&,jsidid,JSObject*){// str_resolve ignores non-integer ids.returnJSID_IS_INT(id);}staticboolstr_resolve(JSContext*cx,HandleObjectobj,HandleIdid,bool*resolvedp){if(!JSID_IS_INT(id))returntrue;RootedStringstr(cx,obj->as<StringObject>().unbox());int32_tslot=JSID_TO_INT(id);if((size_t)slot<str->length()){JSString*str1=cx->staticStrings().getUnitStringForElement(cx,str,size_t(slot));if(!str1)returnfalse;RootedValuevalue(cx,StringValue(str1));if(!DefineElement(cx,obj,uint32_t(slot),value,nullptr,nullptr,STRING_ELEMENT_ATTRS|JSPROP_RESOLVING)){returnfalse;}*resolvedp=true;}returntrue;}staticconstClassOpsStringObjectClassOps={nullptr,/* addProperty */nullptr,/* delProperty */nullptr,/* getProperty */nullptr,/* setProperty */str_enumerate,nullptr,/* newEnumerate */str_resolve,str_mayResolve};constClassStringObject::class_={js_String_str,JSCLASS_HAS_RESERVED_SLOTS(StringObject::RESERVED_SLOTS)|JSCLASS_HAS_CACHED_PROTO(JSProto_String),&StringObjectClassOps};/* * Perform the initial |RequireObjectCoercible(thisv)| and |ToString(thisv)| * from nearly all String.prototype.* functions. */staticMOZ_ALWAYS_INLINEJSString*ToStringForStringFunction(JSContext*cx,HandleValuethisv){if(!CheckRecursionLimit(cx))returnnullptr;if(thisv.isString())returnthisv.toString();if(thisv.isObject()){RootedObjectobj(cx,&thisv.toObject());if(obj->is<StringObject>()){StringObject*nobj=&obj->as<StringObject>();// We have to make sure that the ToPrimitive call from ToString// would be unobservable.if(HasNoToPrimitiveMethodPure(nobj,cx)&&HasNativeMethodPure(nobj,cx->names().toString,str_toString,cx)){returnnobj->unbox();}}}elseif(thisv.isNullOrUndefined()){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_CANT_CONVERT_TO,thisv.isNull()?"null":"undefined","object");returnnullptr;}returnToStringSlow<CanGC>(cx,thisv);}MOZ_ALWAYS_INLINEboolIsString(HandleValuev){returnv.isString()||(v.isObject()&&v.toObject().is<StringObject>());}#if JS_HAS_TOSOURCEMOZ_ALWAYS_INLINEboolstr_toSource_impl(JSContext*cx,constCallArgs&args){MOZ_ASSERT(IsString(args.thisv()));Rooted<JSString*>str(cx,ToString<CanGC>(cx,args.thisv()));if(!str)returnfalse;str=QuoteString(cx,str,'"');if(!str)returnfalse;StringBuffersb(cx);if(!sb.append("(new String(")||!sb.append(str)||!sb.append("))"))returnfalse;str=sb.finishString();if(!str)returnfalse;args.rval().setString(str);returntrue;}staticboolstr_toSource(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);returnCallNonGenericMethod<IsString,str_toSource_impl>(cx,args);}#endif /* JS_HAS_TOSOURCE */MOZ_ALWAYS_INLINEboolstr_toString_impl(JSContext*cx,constCallArgs&args){MOZ_ASSERT(IsString(args.thisv()));args.rval().setString(args.thisv().isString()?args.thisv().toString():args.thisv().toObject().as<StringObject>().unbox());returntrue;}booljs::str_toString(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);returnCallNonGenericMethod<IsString,str_toString_impl>(cx,args);}/* * Java-like string native methods. */JSString*js::SubstringKernel(JSContext*cx,HandleStringstr,int32_tbeginInt,int32_tlengthInt){MOZ_ASSERT(0<=beginInt);MOZ_ASSERT(0<=lengthInt);MOZ_ASSERT(uint32_t(beginInt)<=str->length());MOZ_ASSERT(uint32_t(lengthInt)<=str->length()-beginInt);uint32_tbegin=beginInt;uint32_tlen=lengthInt;/* * Optimization for one level deep ropes. * This is common for the following pattern: * * while() { * text = text.substr(0, x) + "bla" + text.substr(x) * test.charCodeAt(x + 1) * } */if(str->isRope()){JSRope*rope=&str->asRope();/* Substring is totally in leftChild of rope. */if(begin+len<=rope->leftChild()->length())returnNewDependentString(cx,rope->leftChild(),begin,len);/* Substring is totally in rightChild of rope. */if(begin>=rope->leftChild()->length()){begin-=rope->leftChild()->length();returnNewDependentString(cx,rope->rightChild(),begin,len);}/* * Requested substring is partly in the left and partly in right child. * Create a rope of substrings for both childs. */MOZ_ASSERT(begin<rope->leftChild()->length()&&begin+len>rope->leftChild()->length());size_tlhsLength=rope->leftChild()->length()-begin;size_trhsLength=begin+len-rope->leftChild()->length();Rooted<JSRope*>ropeRoot(cx,rope);RootedStringlhs(cx,NewDependentString(cx,ropeRoot->leftChild(),begin,lhsLength));if(!lhs)returnnullptr;RootedStringrhs(cx,NewDependentString(cx,ropeRoot->rightChild(),0,rhsLength));if(!rhs)returnnullptr;returnJSRope::new_<CanGC>(cx,lhs,rhs,len);}returnNewDependentString(cx,str,begin,len);}template<typenameCharT>structMaximumInlineLength;template<>structMaximumInlineLength<Latin1Char>{staticconstexprsize_tvalue=JSFatInlineString::MAX_LENGTH_LATIN1;};template<>structMaximumInlineLength<char16_t>{staticconstexprsize_tvalue=JSFatInlineString::MAX_LENGTH_TWO_BYTE;};// Character buffer class used for ToLowerCase and ToUpperCase operations.//// Case conversion operations normally return a string with the same length as// the input string. To avoid over-allocation, we optimistically allocate an// array with same size as the input string and only when we detect special// casing characters, which can change the output string length, we reallocate// the output buffer to the final string length.//// As a further mean to improve runtime performance, the character buffer// contains an inline storage, so we don't need to heap-allocate an array when// a JSInlineString will be used for the output string.//// Why not use mozilla::Vector instead? mozilla::Vector doesn't provide enough// fine-grained control to avoid over-allocation when (re)allocating for exact// buffer sizes. This led to visible performance regressions in µ-benchmarks.template<typenameCharT>classMOZ_NON_PARAMInlineCharBuffer{usingCharPtr=UniquePtr<CharT[],JS::FreePolicy>;staticconstexprsize_tInlineCapacity=MaximumInlineLength<CharT>::value;CharTinlineStorage[InlineCapacity];CharPtrheapStorage;#ifdef DEBUG// In debug mode, we keep track of the requested string lengths to ensure// all character buffer methods are called in the correct order and with// the expected argument values.size_tlastRequestedLength=0;voidassertValidRequest(size_texpectedLastLength,size_tlength){MOZ_ASSERT(length>expectedLastLength,"cannot shrink requested length");MOZ_ASSERT(lastRequestedLength==expectedLastLength);lastRequestedLength=length;}#elsevoidassertValidRequest(size_texpectedLastLength,size_tlength){}#endifpublic:CharT*get(){returnheapStorage?heapStorage.get():inlineStorage;}boolmaybeAlloc(JSContext*cx,size_tlength){assertValidRequest(0,length);if(length<=InlineCapacity)returntrue;MOZ_ASSERT(!heapStorage,"heap storage already allocated");heapStorage=cx->make_pod_array<CharT>(length+1);return!!heapStorage;}boolmaybeRealloc(JSContext*cx,size_toldLength,size_tnewLength){assertValidRequest(oldLength,newLength);if(newLength<=InlineCapacity)returntrue;if(!heapStorage){heapStorage=cx->make_pod_array<CharT>(newLength+1);if(!heapStorage)returnfalse;MOZ_ASSERT(oldLength<=InlineCapacity);PodCopy(heapStorage.get(),inlineStorage,oldLength);returntrue;}CharT*oldChars=heapStorage.release();CharT*newChars=cx->pod_realloc(oldChars,oldLength+1,newLength+1);if(!newChars){js_free(oldChars);returnfalse;}heapStorage.reset(newChars);returntrue;}JSString*toString(JSContext*cx,size_tlength){MOZ_ASSERT(length==lastRequestedLength);if(JSInlineString::lengthFits<CharT>(length)){MOZ_ASSERT(!heapStorage,"expected only inline storage when length fits in inline string");mozilla::Range<constCharT>range(inlineStorage,length);returnNewInlineString<CanGC>(cx,range);}MOZ_ASSERT(heapStorage,"heap storage was not allocated for non-inline string");heapStorage.get()[length]='\0';// Null-terminateJSString*res=NewStringDontDeflate<CanGC>(cx,heapStorage.get(),length);if(!res)returnnullptr;mozilla::Unused<<heapStorage.release();returnres;}};/** * U+03A3 GREEK CAPITAL LETTER SIGMA has two different lower case mappings * depending on its context: * When it's preceded by a cased character and not followed by another cased * character, its lower case form is U+03C2 GREEK SMALL LETTER FINAL SIGMA. * Otherwise its lower case mapping is U+03C3 GREEK SMALL LETTER SIGMA. * * Unicode 9.0, §3.13 Default Case Algorithms */staticchar16_tFinal_Sigma(constchar16_t*chars,size_tlength,size_tindex){MOZ_ASSERT(index<length);MOZ_ASSERT(chars[index]==unicode::GREEK_CAPITAL_LETTER_SIGMA);MOZ_ASSERT(unicode::ToLowerCase(unicode::GREEK_CAPITAL_LETTER_SIGMA)==unicode::GREEK_SMALL_LETTER_SIGMA);#if ENABLE_INTL_API// Tell the analysis the BinaryProperty.contains function pointer called by// u_hasBinaryProperty cannot GC.JS::AutoSuppressGCAnalysisnogc;boolprecededByCased=false;for(size_ti=index;i>0;){char16_tc=chars[--i];uint32_tcodePoint=c;if(unicode::IsTrailSurrogate(c)&&i>0){char16_tlead=chars[i-1];if(unicode::IsLeadSurrogate(lead)){codePoint=unicode::UTF16Decode(lead,c);i--;}}// Ignore any characters with the property Case_Ignorable.// NB: We need to skip over all Case_Ignorable characters, even when// they also have the Cased binary property.if(u_hasBinaryProperty(codePoint,UCHAR_CASE_IGNORABLE))continue;precededByCased=u_hasBinaryProperty(codePoint,UCHAR_CASED);break;}if(!precededByCased)returnunicode::GREEK_SMALL_LETTER_SIGMA;boolfollowedByCased=false;for(size_ti=index+1;i<length;){char16_tc=chars[i++];uint32_tcodePoint=c;if(unicode::IsLeadSurrogate(c)&&i<length){char16_ttrail=chars[i];if(unicode::IsTrailSurrogate(trail)){codePoint=unicode::UTF16Decode(c,trail);i++;}}// Ignore any characters with the property Case_Ignorable.// NB: We need to skip over all Case_Ignorable characters, even when// they also have the Cased binary property.if(u_hasBinaryProperty(codePoint,UCHAR_CASE_IGNORABLE))continue;followedByCased=u_hasBinaryProperty(codePoint,UCHAR_CASED);break;}if(!followedByCased)returnunicode::GREEK_SMALL_LETTER_FINAL_SIGMA;#endifreturnunicode::GREEK_SMALL_LETTER_SIGMA;}staticLatin1CharFinal_Sigma(constLatin1Char*chars,size_tlength,size_tindex){MOZ_ASSERT_UNREACHABLE("U+03A3 is not a Latin-1 character");return0;}// If |srcLength == destLength| is true, the destination buffer was allocated// with the same size as the source buffer. When we append characters which// have special casing mappings, we test |srcLength == destLength| to decide// if we need to back out and reallocate a sufficiently large destination// buffer. Otherwise the destination buffer was allocated with the correct// size to hold all lower case mapped characters, i.e.// |destLength == ToLowerCaseLength(srcChars, 0, srcLength)| is true.template<typenameCharT>staticsize_tToLowerCaseImpl(CharT*destChars,constCharT*srcChars,size_tstartIndex,size_tsrcLength,size_tdestLength){MOZ_ASSERT(startIndex<srcLength);MOZ_ASSERT(srcLength<=destLength);MOZ_ASSERT_IF((IsSame<CharT,Latin1Char>::value),srcLength==destLength);size_tj=startIndex;for(size_ti=startIndex;i<srcLength;i++){char16_tc=srcChars[i];if(!IsSame<CharT,Latin1Char>::value){if(unicode::IsLeadSurrogate(c)&&i+1<srcLength){char16_ttrail=srcChars[i+1];if(unicode::IsTrailSurrogate(trail)){trail=unicode::ToLowerCaseNonBMPTrail(c,trail);destChars[j++]=c;destChars[j++]=trail;i++;continue;}}// Special case: U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE// lowercases to <U+0069 U+0307>.if(c==unicode::LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE){// Return if the output buffer is too small.if(srcLength==destLength)returni;destChars[j++]=CharT('i');destChars[j++]=CharT(unicode::COMBINING_DOT_ABOVE);continue;}// Special case: U+03A3 GREEK CAPITAL LETTER SIGMA lowercases to// one of two codepoints depending on context.if(c==unicode::GREEK_CAPITAL_LETTER_SIGMA){destChars[j++]=Final_Sigma(srcChars,srcLength,i);continue;}}c=unicode::ToLowerCase(c);MOZ_ASSERT_IF((IsSame<CharT,Latin1Char>::value),c<=JSString::MAX_LATIN1_CHAR);destChars[j++]=c;}MOZ_ASSERT(j==destLength);returnsrcLength;}staticsize_tToLowerCaseLength(constchar16_t*chars,size_tstartIndex,size_tlength){size_tlowerLength=length;for(size_ti=startIndex;i<length;i++){char16_tc=chars[i];// U+0130 is lowercased to the two-element sequence <U+0069 U+0307>.if(c==unicode::LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE)lowerLength+=1;}returnlowerLength;}staticsize_tToLowerCaseLength(constLatin1Char*chars,size_tstartIndex,size_tlength){MOZ_ASSERT_UNREACHABLE("never called for Latin-1 strings");return0;}template<typenameCharT>staticJSString*ToLowerCase(JSContext*cx,JSLinearString*str){// Unlike toUpperCase, toLowerCase has the nice invariant that if the// input is a Latin-1 string, the output is also a Latin-1 string.InlineCharBuffer<CharT>newChars;constsize_tlength=str->length();size_tresultLength;{AutoCheckCannotGCnogc;constCharT*chars=str->chars<CharT>(nogc);// We don't need extra special casing checks in the loop below,// because U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE and U+03A3// GREEK CAPITAL LETTER SIGMA already have simple lower case mappings.MOZ_ASSERT(unicode::CanLowerCase(unicode::LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE),"U+0130 has a simple lower case mapping");MOZ_ASSERT(unicode::CanLowerCase(unicode::GREEK_CAPITAL_LETTER_SIGMA),"U+03A3 has a simple lower case mapping");// One element Latin-1 strings can be directly retrieved from the// static strings cache.if(IsSame<CharT,Latin1Char>::value){if(length==1){char16_tlower=unicode::ToLowerCase(chars[0]);MOZ_ASSERT(lower<=JSString::MAX_LATIN1_CHAR);MOZ_ASSERT(StaticStrings::hasUnit(lower));returncx->staticStrings().getUnit(lower);}}// Look for the first character that changes when lowercased.size_ti=0;for(;i<length;i++){char16_tc=chars[i];if(!IsSame<CharT,Latin1Char>::value){if(unicode::IsLeadSurrogate(c)&&i+1<length){char16_ttrail=chars[i+1];if(unicode::IsTrailSurrogate(trail)){if(unicode::CanLowerCaseNonBMP(c,trail))break;i++;continue;}}}if(unicode::CanLowerCase(c))break;}// If no character needs to change, return the input string.if(i==length)returnstr;resultLength=length;if(!newChars.maybeAlloc(cx,resultLength))returnnullptr;PodCopy(newChars.get(),chars,i);size_treadChars=ToLowerCaseImpl(newChars.get(),chars,i,length,resultLength);if(readChars<length){MOZ_ASSERT((!IsSame<CharT,Latin1Char>::value),"Latin-1 strings don't have special lower case mappings");resultLength=ToLowerCaseLength(chars,readChars,length);if(!newChars.maybeRealloc(cx,length,resultLength))returnnullptr;MOZ_ALWAYS_TRUE(length==ToLowerCaseImpl(newChars.get(),chars,readChars,length,resultLength));}}returnnewChars.toString(cx,resultLength);}JSString*js::StringToLowerCase(JSContext*cx,HandleLinearStringstring){if(string->hasLatin1Chars())returnToLowerCase<Latin1Char>(cx,string);returnToLowerCase<char16_t>(cx,string);}booljs::str_toLowerCase(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedStringstr(cx,ToStringForStringFunction(cx,args.thisv()));if(!str)returnfalse;RootedLinearStringlinear(cx,str->ensureLinear(cx));if(!linear)returnfalse;JSString*result=StringToLowerCase(cx,linear);if(!result)returnfalse;args.rval().setString(result);returntrue;}#if !EXPOSE_INTL_APIbooljs::str_toLocaleLowerCase(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedStringstr(cx,ToStringForStringFunction(cx,args.thisv()));if(!str)returnfalse;/* * Forcefully ignore the first (or any) argument and return toLowerCase(), * ECMA has reserved that argument, presumably for defining the locale. */if(cx->runtime()->localeCallbacks&&cx->runtime()->localeCallbacks->localeToLowerCase){RootedValueresult(cx);if(!cx->runtime()->localeCallbacks->localeToLowerCase(cx,str,&result))returnfalse;args.rval().set(result);returntrue;}RootedLinearStringlinear(cx,str->ensureLinear(cx));if(!linear)returnfalse;JSString*result=StringToLowerCase(cx,linear);if(!result)returnfalse;args.rval().setString(result);returntrue;}#endif /* !EXPOSE_INTL_API */staticinlineboolCanUpperCaseSpecialCasing(Latin1CharcharCode){// Handle U+00DF LATIN SMALL LETTER SHARP S inline, all other Latin-1// characters don't have special casing rules.MOZ_ASSERT_IF(charCode!=unicode::LATIN_SMALL_LETTER_SHARP_S,!unicode::CanUpperCaseSpecialCasing(charCode));returncharCode==unicode::LATIN_SMALL_LETTER_SHARP_S;}staticinlineboolCanUpperCaseSpecialCasing(char16_tcharCode){returnunicode::CanUpperCaseSpecialCasing(charCode);}staticinlinesize_tLengthUpperCaseSpecialCasing(Latin1CharcharCode){// U+00DF LATIN SMALL LETTER SHARP S is uppercased to two 'S'.MOZ_ASSERT(charCode==unicode::LATIN_SMALL_LETTER_SHARP_S);return2;}staticinlinesize_tLengthUpperCaseSpecialCasing(char16_tcharCode){MOZ_ASSERT(CanUpperCaseSpecialCasing(charCode));returnunicode::LengthUpperCaseSpecialCasing(charCode);}staticinlinevoidAppendUpperCaseSpecialCasing(char16_tcharCode,Latin1Char*elements,size_t*index){// U+00DF LATIN SMALL LETTER SHARP S is uppercased to two 'S'.MOZ_ASSERT(charCode==unicode::LATIN_SMALL_LETTER_SHARP_S);static_assert('S'<=JSString::MAX_LATIN1_CHAR,"'S' is a Latin-1 character");elements[(*index)++]='S';elements[(*index)++]='S';}staticinlinevoidAppendUpperCaseSpecialCasing(char16_tcharCode,char16_t*elements,size_t*index){unicode::AppendUpperCaseSpecialCasing(charCode,elements,index);}// See ToLowerCaseImpl for an explanation of the parameters.template<typenameDestChar,typenameSrcChar>staticsize_tToUpperCaseImpl(DestChar*destChars,constSrcChar*srcChars,size_tstartIndex,size_tsrcLength,size_tdestLength){static_assert(IsSame<SrcChar,Latin1Char>::value||!IsSame<DestChar,Latin1Char>::value,"cannot write non-Latin-1 characters into Latin-1 string");MOZ_ASSERT(startIndex<srcLength);MOZ_ASSERT(srcLength<=destLength);size_tj=startIndex;for(size_ti=startIndex;i<srcLength;i++){char16_tc=srcChars[i];if(!IsSame<DestChar,Latin1Char>::value){if(unicode::IsLeadSurrogate(c)&&i+1<srcLength){char16_ttrail=srcChars[i+1];if(unicode::IsTrailSurrogate(trail)){trail=unicode::ToUpperCaseNonBMPTrail(c,trail);destChars[j++]=c;destChars[j++]=trail;i++;continue;}}}if(MOZ_UNLIKELY(c>0x7f&&CanUpperCaseSpecialCasing(static_cast<SrcChar>(c)))){// Return if the output buffer is too small.if(srcLength==destLength)returni;AppendUpperCaseSpecialCasing(c,destChars,&j);continue;}c=unicode::ToUpperCase(c);MOZ_ASSERT_IF((IsSame<DestChar,Latin1Char>::value),c<=JSString::MAX_LATIN1_CHAR);destChars[j++]=c;}MOZ_ASSERT(j==destLength);returnsrcLength;}// Explicit instantiation so we don't hit the static_assert from above.staticboolToUpperCaseImpl(Latin1Char*destChars,constchar16_t*srcChars,size_tstartIndex,size_tsrcLength,size_tdestLength){MOZ_ASSERT_UNREACHABLE("cannot write non-Latin-1 characters into Latin-1 string");returnfalse;}template<typenameCharT>staticsize_tToUpperCaseLength(constCharT*chars,size_tstartIndex,size_tlength){size_tupperLength=length;for(size_ti=startIndex;i<length;i++){char16_tc=chars[i];if(c>0x7f&&CanUpperCaseSpecialCasing(static_cast<CharT>(c)))upperLength+=LengthUpperCaseSpecialCasing(static_cast<CharT>(c))-1;}returnupperLength;}template<typenameDestChar,typenameSrcChar>staticinlinevoidCopyChars(DestChar*destChars,constSrcChar*srcChars,size_tlength){static_assert(!IsSame<DestChar,SrcChar>::value,"PodCopy is used for the same type case");for(size_ti=0;i<length;i++)destChars[i]=srcChars[i];}template<typenameCharT>staticinlinevoidCopyChars(CharT*destChars,constCharT*srcChars,size_tlength){PodCopy(destChars,srcChars,length);}template<typenameDestChar,typenameSrcChar>staticinlineboolToUpperCase(JSContext*cx,InlineCharBuffer<DestChar>&newChars,constSrcChar*chars,size_tstartIndex,size_tlength,size_t*resultLength){MOZ_ASSERT(startIndex<length);*resultLength=length;if(!newChars.maybeAlloc(cx,length))returnfalse;CopyChars(newChars.get(),chars,startIndex);size_treadChars=ToUpperCaseImpl(newChars.get(),chars,startIndex,length,length);if(readChars<length){size_tactualLength=ToUpperCaseLength(chars,readChars,length);*resultLength=actualLength;if(!newChars.maybeRealloc(cx,length,actualLength))returnfalse;MOZ_ALWAYS_TRUE(length==ToUpperCaseImpl(newChars.get(),chars,readChars,length,actualLength));}returntrue;}template<typenameCharT>staticJSString*ToUpperCase(JSContext*cx,JSLinearString*str){usingLatin1Buffer=InlineCharBuffer<Latin1Char>;usingTwoByteBuffer=InlineCharBuffer<char16_t>;mozilla::MaybeOneOf<Latin1Buffer,TwoByteBuffer>newChars;constsize_tlength=str->length();size_tresultLength;{AutoCheckCannotGCnogc;constCharT*chars=str->chars<CharT>(nogc);// Most one element Latin-1 strings can be directly retrieved from the// static strings cache.if(IsSame<CharT,Latin1Char>::value){if(length==1){Latin1Charc=chars[0];if(c!=unicode::MICRO_SIGN&&c!=unicode::LATIN_SMALL_LETTER_Y_WITH_DIAERESIS&&c!=unicode::LATIN_SMALL_LETTER_SHARP_S){char16_tupper=unicode::ToUpperCase(c);MOZ_ASSERT(upper<=JSString::MAX_LATIN1_CHAR);MOZ_ASSERT(StaticStrings::hasUnit(upper));returncx->staticStrings().getUnit(upper);}MOZ_ASSERT(unicode::ToUpperCase(c)>JSString::MAX_LATIN1_CHAR||CanUpperCaseSpecialCasing(c));}}// Look for the first character that changes when uppercased.size_ti=0;for(;i<length;i++){char16_tc=chars[i];if(!IsSame<CharT,Latin1Char>::value){if(unicode::IsLeadSurrogate(c)&&i+1<length){char16_ttrail=chars[i+1];if(unicode::IsTrailSurrogate(trail)){if(unicode::CanUpperCaseNonBMP(c,trail))break;i++;continue;}}}if(unicode::CanUpperCase(c))break;if(MOZ_UNLIKELY(c>0x7f&&CanUpperCaseSpecialCasing(static_cast<CharT>(c))))break;}// If no character needs to change, return the input string.if(i==length)returnstr;// The string changes when uppercased, so we must create a new string.// Can it be Latin-1?//// If the original string is Latin-1, it can -- unless the string// contains U+00B5 MICRO SIGN or U+00FF SMALL LETTER Y WITH DIAERESIS,// the only Latin-1 codepoints that don't uppercase within Latin-1.// Search for those codepoints to decide whether the new string can be// Latin-1.// If the original string is a two-byte string, its uppercase form is// so rarely Latin-1 that we don't even consider creating a new// Latin-1 string.boolresultIsLatin1;if(IsSame<CharT,Latin1Char>::value){resultIsLatin1=true;for(size_tj=i;j<length;j++){Latin1Charc=chars[j];if(c==unicode::MICRO_SIGN||c==unicode::LATIN_SMALL_LETTER_Y_WITH_DIAERESIS){MOZ_ASSERT(unicode::ToUpperCase(c)>JSString::MAX_LATIN1_CHAR);resultIsLatin1=false;break;}else{MOZ_ASSERT(unicode::ToUpperCase(c)<=JSString::MAX_LATIN1_CHAR);}}}else{resultIsLatin1=false;}if(resultIsLatin1){newChars.construct<Latin1Buffer>();if(!ToUpperCase(cx,newChars.ref<Latin1Buffer>(),chars,i,length,&resultLength))returnnullptr;}else{newChars.construct<TwoByteBuffer>();if(!ToUpperCase(cx,newChars.ref<TwoByteBuffer>(),chars,i,length,&resultLength))returnnullptr;}}returnnewChars.constructed<Latin1Buffer>()?newChars.ref<Latin1Buffer>().toString(cx,resultLength):newChars.ref<TwoByteBuffer>().toString(cx,resultLength);}JSString*js::StringToUpperCase(JSContext*cx,HandleLinearStringstring){if(string->hasLatin1Chars())returnToUpperCase<Latin1Char>(cx,string);returnToUpperCase<char16_t>(cx,string);}booljs::str_toUpperCase(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedStringstr(cx,ToStringForStringFunction(cx,args.thisv()));if(!str)returnfalse;RootedLinearStringlinear(cx,str->ensureLinear(cx));if(!linear)returnfalse;JSString*result=StringToUpperCase(cx,linear);if(!result)returnfalse;args.rval().setString(result);returntrue;}#if !EXPOSE_INTL_APIbooljs::str_toLocaleUpperCase(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedStringstr(cx,ToStringForStringFunction(cx,args.thisv()));if(!str)returnfalse;/* * Forcefully ignore the first (or any) argument and return toUpperCase(), * ECMA has reserved that argument, presumably for defining the locale. */if(cx->runtime()->localeCallbacks&&cx->runtime()->localeCallbacks->localeToUpperCase){RootedValueresult(cx);if(!cx->runtime()->localeCallbacks->localeToUpperCase(cx,str,&result))returnfalse;args.rval().set(result);returntrue;}RootedLinearStringlinear(cx,str->ensureLinear(cx));if(!linear)returnfalse;JSString*result=StringToUpperCase(cx,linear);if(!result)returnfalse;args.rval().setString(result);returntrue;}#endif /* !EXPOSE_INTL_API */#if !EXPOSE_INTL_APIbooljs::str_localeCompare(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedStringstr(cx,ToStringForStringFunction(cx,args.thisv()));if(!str)returnfalse;RootedStringthatStr(cx,ToString<CanGC>(cx,args.get(0)));if(!thatStr)returnfalse;if(cx->runtime()->localeCallbacks&&cx->runtime()->localeCallbacks->localeCompare){RootedValueresult(cx);if(!cx->runtime()->localeCallbacks->localeCompare(cx,str,thatStr,&result))returnfalse;args.rval().set(result);returntrue;}int32_tresult;if(!CompareStrings(cx,str,thatStr,&result))returnfalse;args.rval().setInt32(result);returntrue;}#endif#if EXPOSE_INTL_API// ES2017 draft rev 45e890512fd77add72cc0ee742785f9f6f6482de// 21.1.3.12 String.prototype.normalize ( [ form ] )booljs::str_normalize(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);// Steps 1-2.RootedStringstr(cx,ToStringForStringFunction(cx,args.thisv()));if(!str)returnfalse;enumNormalizationForm{NFC,NFD,NFKC,NFKD};NormalizationFormform;if(!args.hasDefined(0)){// Step 3.form=NFC;}else{// Step 4.RootedLinearStringformStr(cx,ArgToRootedString(cx,args,0));if(!formStr)returnfalse;// Step 5.if(EqualStrings(formStr,cx->names().NFC)){form=NFC;}elseif(EqualStrings(formStr,cx->names().NFD)){form=NFD;}elseif(EqualStrings(formStr,cx->names().NFKC)){form=NFKC;}elseif(EqualStrings(formStr,cx->names().NFKD)){form=NFKD;}else{JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INVALID_NORMALIZE_FORM);returnfalse;}}// Latin-1 strings are already in Normalization Form C.if(form==NFC&&str->hasLatin1Chars()){// Step 7.args.rval().setString(str);returntrue;}// Step 6.AutoStableStringCharsstableChars(cx);if(!stableChars.initTwoByte(cx,str))returnfalse;mozilla::Range<constchar16_t>srcChars=stableChars.twoByteRange();// The unorm2_getXXXInstance() methods return a shared instance which must// not be deleted.UErrorCodestatus=U_ZERO_ERROR;constUNormalizer2*normalizer;if(form==NFC){normalizer=unorm2_getNFCInstance(&status);}elseif(form==NFD){normalizer=unorm2_getNFDInstance(&status);}elseif(form==NFKC){normalizer=unorm2_getNFKCInstance(&status);}else{MOZ_ASSERT(form==NFKD);normalizer=unorm2_getNFKDInstance(&status);}if(U_FAILURE(status)){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INTERNAL_INTL_ERROR);returnfalse;}int32_tspanLength=unorm2_spanQuickCheckYes(normalizer,srcChars.begin().get(),srcChars.length(),&status);if(U_FAILURE(status)){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INTERNAL_INTL_ERROR);returnfalse;}MOZ_ASSERT(0<=spanLength&&size_t(spanLength)<=srcChars.length());// Return if the input string is already normalized.if(size_t(spanLength)==srcChars.length()){// Step 7.args.rval().setString(str);returntrue;}staticconstsize_tINLINE_CAPACITY=32;Vector<char16_t,INLINE_CAPACITY>chars(cx);if(!chars.resize(Max(INLINE_CAPACITY,srcChars.length())))returnfalse;// Copy the already normalized prefix.if(spanLength>0)PodCopy(chars.begin(),srcChars.begin().get(),size_t(spanLength));mozilla::RangedPtr<constchar16_t>remainingStart=srcChars.begin()+spanLength;size_tremainingLength=srcChars.length()-size_t(spanLength);int32_tsize=unorm2_normalizeSecondAndAppend(normalizer,chars.begin(),spanLength,chars.length(),remainingStart.get(),remainingLength,&status);if(status==U_BUFFER_OVERFLOW_ERROR){MOZ_ASSERT(size>=0);if(!chars.resize(size))returnfalse;status=U_ZERO_ERROR;#ifdef DEBUGint32_tfinalSize=#endifunorm2_normalizeSecondAndAppend(normalizer,chars.begin(),spanLength,chars.length(),remainingStart.get(),remainingLength,&status);MOZ_ASSERT_IF(!U_FAILURE(status),size==finalSize);}if(U_FAILURE(status)){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INTERNAL_INTL_ERROR);returnfalse;}MOZ_ASSERT(size>=0);JSString*ns=NewStringCopyN<CanGC>(cx,chars.begin(),size);if(!ns)returnfalse;// Step 7.args.rval().setString(ns);returntrue;}#endifbooljs::str_charAt(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedStringstr(cx);size_ti;if(args.thisv().isString()&&args.length()!=0&&args[0].isInt32()){str=args.thisv().toString();i=size_t(args[0].toInt32());if(i>=str->length())gotoout_of_range;}else{str=ToStringForStringFunction(cx,args.thisv());if(!str)returnfalse;doubled=0.0;if(args.length()>0&&!ToInteger(cx,args[0],&d))returnfalse;if(d<0||str->length()<=d)gotoout_of_range;i=size_t(d);}str=cx->staticStrings().getUnitStringForElement(cx,str,i);if(!str)returnfalse;args.rval().setString(str);returntrue;out_of_range:args.rval().setString(cx->runtime()->emptyString);returntrue;}booljs::str_charCodeAt_impl(JSContext*cx,HandleStringstring,HandleValueindex,MutableHandleValueres){RootedStringstr(cx);size_ti;if(index.isInt32()){i=index.toInt32();if(i>=string->length())gotoout_of_range;}else{doubled=0.0;if(!ToInteger(cx,index,&d))returnfalse;// check whether d is negative as size_t is unsignedif(d<0||string->length()<=d)gotoout_of_range;i=size_t(d);}char16_tc;if(!string->getChar(cx,i,&c))returnfalse;res.setInt32(c);returntrue;out_of_range:res.setNaN();returntrue;}booljs::str_charCodeAt(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedStringstr(cx);RootedValueindex(cx);if(args.thisv().isString()){str=args.thisv().toString();}else{str=ToStringForStringFunction(cx,args.thisv());if(!str)returnfalse;}if(args.length()!=0)index=args[0];elseindex.setInt32(0);returnjs::str_charCodeAt_impl(cx,str,index,args.rval());}/* * Boyer-Moore-Horspool superlinear search for pat:patlen in text:textlen. * The patlen argument must be positive and no greater than sBMHPatLenMax. * * Return the index of pat in text, or -1 if not found. */staticconstuint32_tsBMHCharSetSize=256;/* ISO-Latin-1 */staticconstuint32_tsBMHPatLenMax=255;/* skip table element is uint8_t */staticconstintsBMHBadPattern=-2;/* return value if pat is not ISO-Latin-1 */template<typenameTextChar,typenamePatChar>staticintBoyerMooreHorspool(constTextChar*text,uint32_ttextLen,constPatChar*pat,uint32_tpatLen){MOZ_ASSERT(0<patLen&&patLen<=sBMHPatLenMax);uint8_tskip[sBMHCharSetSize];for(uint32_ti=0;i<sBMHCharSetSize;i++)skip[i]=uint8_t(patLen);uint32_tpatLast=patLen-1;for(uint32_ti=0;i<patLast;i++){char16_tc=pat[i];if(c>=sBMHCharSetSize)returnsBMHBadPattern;skip[c]=uint8_t(patLast-i);}for(uint32_tk=patLast;k<textLen;){for(uint32_ti=k,j=patLast;;i--,j--){if(text[i]!=pat[j])break;if(j==0)returnstatic_cast<int>(i);/* safe: max string size */}char16_tc=text[k];k+=(c>=sBMHCharSetSize)?patLen:skip[c];}return-1;}template<typenameTextChar,typenamePatChar>structMemCmp{typedefuint32_tExtent;staticMOZ_ALWAYS_INLINEExtentcomputeExtent(constPatChar*,uint32_tpatLen){return(patLen-1)*sizeof(PatChar);}staticMOZ_ALWAYS_INLINEboolmatch(constPatChar*p,constTextChar*t,Extentextent){MOZ_ASSERT(sizeof(TextChar)==sizeof(PatChar));returnmemcmp(p,t,extent)==0;}};template<typenameTextChar,typenamePatChar>structManualCmp{typedefconstPatChar*Extent;staticMOZ_ALWAYS_INLINEExtentcomputeExtent(constPatChar*pat,uint32_tpatLen){returnpat+patLen;}staticMOZ_ALWAYS_INLINEboolmatch(constPatChar*p,constTextChar*t,Extentextent){for(;p!=extent;++p,++t){if(*p!=*t)returnfalse;}returntrue;}};template<typenameTextChar,typenamePatChar>staticconstTextChar*FirstCharMatcherUnrolled(constTextChar*text,uint32_tn,constPatCharpat){constTextChar*textend=text+n;constTextChar*t=text;switch((textend-t)&7){case0:if(*t++==pat)returnt-1;MOZ_FALLTHROUGH;case7:if(*t++==pat)returnt-1;MOZ_FALLTHROUGH;case6:if(*t++==pat)returnt-1;MOZ_FALLTHROUGH;case5:if(*t++==pat)returnt-1;MOZ_FALLTHROUGH;case4:if(*t++==pat)returnt-1;MOZ_FALLTHROUGH;case3:if(*t++==pat)returnt-1;MOZ_FALLTHROUGH;case2:if(*t++==pat)returnt-1;MOZ_FALLTHROUGH;case1:if(*t++==pat)returnt-1;}while(textend!=t){if(t[0]==pat)returnt;if(t[1]==pat)returnt+1;if(t[2]==pat)returnt+2;if(t[3]==pat)returnt+3;if(t[4]==pat)returnt+4;if(t[5]==pat)returnt+5;if(t[6]==pat)returnt+6;if(t[7]==pat)returnt+7;t+=8;}returnnullptr;}staticconstchar*FirstCharMatcher8bit(constchar*text,uint32_tn,constcharpat){returnreinterpret_cast<constchar*>(memchr(text,pat,n));}template<classInnerMatch,typenameTextChar,typenamePatChar>staticintMatcher(constTextChar*text,uint32_ttextlen,constPatChar*pat,uint32_tpatlen){MOZ_ASSERT(patlen>0);if(sizeof(TextChar)==1&&sizeof(PatChar)>1&&pat[0]>0xff)return-1;consttypenameInnerMatch::Extentextent=InnerMatch::computeExtent(pat,patlen);uint32_ti=0;uint32_tn=textlen-patlen+1;while(i<n){constTextChar*pos;if(sizeof(TextChar)==1){MOZ_ASSERT(pat[0]<=0xff);pos=(TextChar*)FirstCharMatcher8bit((char*)text+i,n-i,pat[0]);}else{pos=FirstCharMatcherUnrolled(text+i,n-i,char16_t(pat[0]));}if(pos==nullptr)return-1;i=static_cast<uint32_t>(pos-text);if(InnerMatch::match(pat+1,text+i+1,extent))returni;i+=1;}return-1;}template<typenameTextChar,typenamePatChar>staticMOZ_ALWAYS_INLINEintStringMatch(constTextChar*text,uint32_ttextLen,constPatChar*pat,uint32_tpatLen){if(patLen==0)return0;if(textLen<patLen)return-1;#if defined(__i386__) || defined(_M_IX86) || defined(__i386)/* * Given enough registers, the unrolled loop below is faster than the * following loop. 32-bit x86 does not have enough registers. */if(patLen==1){constPatCharp0=*pat;constTextChar*end=text+textLen;for(constTextChar*c=text;c!=end;++c){if(*c==p0)returnc-text;}return-1;}#endif/* * If the text or pattern string is short, BMH will be more expensive than * the basic linear scan due to initialization cost and a more complex loop * body. While the correct threshold is input-dependent, we can make a few * conservative observations: * - When |textLen| is "big enough", the initialization time will be * proportionally small, so the worst-case slowdown is minimized. * - When |patLen| is "too small", even the best case for BMH will be * slower than a simple scan for large |textLen| due to the more complex * loop body of BMH. * From this, the values for "big enough" and "too small" are determined * empirically. See bug 526348. */if(textLen>=512&&patLen>=11&&patLen<=sBMHPatLenMax){intindex=BoyerMooreHorspool(text,textLen,pat,patLen);if(index!=sBMHBadPattern)returnindex;}/* * For big patterns with large potential overlap we want the SIMD-optimized * speed of memcmp. For small patterns, a simple loop is faster. We also can't * use memcmp if one of the strings is TwoByte and the other is Latin-1. * * FIXME: Linux memcmp performance is sad and the manual loop is faster. */return#if !defined(__linux__)(patLen>128&&IsSame<TextChar,PatChar>::value)?Matcher<MemCmp<TextChar,PatChar>,TextChar,PatChar>(text,textLen,pat,patLen):#endifMatcher<ManualCmp<TextChar,PatChar>,TextChar,PatChar>(text,textLen,pat,patLen);}staticint32_tStringMatch(JSLinearString*text,JSLinearString*pat,uint32_tstart=0){MOZ_ASSERT(start<=text->length());uint32_ttextLen=text->length()-start;uint32_tpatLen=pat->length();intmatch;AutoCheckCannotGCnogc;if(text->hasLatin1Chars()){constLatin1Char*textChars=text->latin1Chars(nogc)+start;if(pat->hasLatin1Chars())match=StringMatch(textChars,textLen,pat->latin1Chars(nogc),patLen);elsematch=StringMatch(textChars,textLen,pat->twoByteChars(nogc),patLen);}else{constchar16_t*textChars=text->twoByteChars(nogc)+start;if(pat->hasLatin1Chars())match=StringMatch(textChars,textLen,pat->latin1Chars(nogc),patLen);elsematch=StringMatch(textChars,textLen,pat->twoByteChars(nogc),patLen);}return(match==-1)?-1:start+match;}staticconstsize_tsRopeMatchThresholdRatioLog2=4;booljs::StringHasPattern(JSLinearString*text,constchar16_t*pat,uint32_tpatLen){AutoCheckCannotGCnogc;returntext->hasLatin1Chars()?StringMatch(text->latin1Chars(nogc),text->length(),pat,patLen)!=-1:StringMatch(text->twoByteChars(nogc),text->length(),pat,patLen)!=-1;}intjs::StringFindPattern(JSLinearString*text,JSLinearString*pat,size_tstart){returnStringMatch(text,pat,start);}// When an algorithm does not need a string represented as a single linear// array of characters, this range utility may be used to traverse the string a// sequence of linear arrays of characters. This avoids flattening ropes.classStringSegmentRange{// If malloc() shows up in any profiles from this vector, we can add a new// StackAllocPolicy which stashes a reusable freed-at-gc buffer in the cx.usingStackVector=JS::GCVector<JSString*,16>;Rooted<StackVector>stack;RootedLinearStringcur;boolsettle(JSString*str){while(str->isRope()){JSRope&rope=str->asRope();if(!stack.append(rope.rightChild()))returnfalse;str=rope.leftChild();}cur=&str->asLinear();returntrue;}public:explicitStringSegmentRange(JSContext*cx):stack(cx,StackVector(cx)),cur(cx){}MOZ_MUST_USEboolinit(JSString*str){MOZ_ASSERT(stack.empty());returnsettle(str);}boolempty()const{returncur==nullptr;}JSLinearString*front()const{MOZ_ASSERT(!cur->isRope());returncur;}MOZ_MUST_USEboolpopFront(){MOZ_ASSERT(!empty());if(stack.empty()){cur=nullptr;returntrue;}returnsettle(stack.popCopy());}};typedefVector<JSLinearString*,16,SystemAllocPolicy>LinearStringVector;template<typenameTextChar,typenamePatChar>staticintRopeMatchImpl(constAutoCheckCannotGC&nogc,LinearStringVector&strings,constPatChar*pat,size_tpatLen){/* Absolute offset from the beginning of the logical text string. */intpos=0;for(JSLinearString**outerp=strings.begin();outerp!=strings.end();++outerp){/* Try to find a match within 'outer'. */JSLinearString*outer=*outerp;constTextChar*chars=outer->chars<TextChar>(nogc);size_tlen=outer->length();intmatchResult=StringMatch(chars,len,pat,patLen);if(matchResult!=-1){/* Matched! */returnpos+matchResult;}/* Try to find a match starting in 'outer' and running into other nodes. */constTextChar*consttext=chars+(patLen>len?0:len-patLen+1);constTextChar*consttextend=chars+len;constPatCharp0=*pat;constPatChar*constp1=pat+1;constPatChar*constpatend=pat+patLen;for(constTextChar*t=text;t!=textend;){if(*t++!=p0)continue;JSLinearString**innerp=outerp;constTextChar*ttend=textend;constTextChar*tt=t;for(constPatChar*pp=p1;pp!=patend;++pp,++tt){while(tt==ttend){if(++innerp==strings.end())return-1;JSLinearString*inner=*innerp;tt=inner->chars<TextChar>(nogc);ttend=tt+inner->length();}if(*pp!=*tt)gotobreak_continue;}/* Matched! */returnpos+(t-chars)-1;/* -1 because of *t++ above */break_continue:;}pos+=len;}return-1;}/* * RopeMatch takes the text to search and the pattern to search for in the text. * RopeMatch returns false on OOM and otherwise returns the match index through * the 'match' outparam (-1 for not found). */staticboolRopeMatch(JSContext*cx,JSRope*text,JSLinearString*pat,int*match){uint32_tpatLen=pat->length();if(patLen==0){*match=0;returntrue;}if(text->length()<patLen){*match=-1;returntrue;}/* * List of leaf nodes in the rope. If we run out of memory when trying to * append to this list, we can still fall back to StringMatch, so use the * system allocator so we don't report OOM in that case. */LinearStringVectorstrings;/* * We don't want to do rope matching if there is a poor node-to-char ratio, * since this means spending a lot of time in the match loop below. We also * need to build the list of leaf nodes. Do both here: iterate over the * nodes so long as there are not too many. * * We also don't use rope matching if the rope contains both Latin-1 and * TwoByte nodes, to simplify the match algorithm. */{size_tthreshold=text->length()>>sRopeMatchThresholdRatioLog2;StringSegmentRanger(cx);if(!r.init(text))returnfalse;booltextIsLatin1=text->hasLatin1Chars();while(!r.empty()){if(threshold--==0||r.front()->hasLatin1Chars()!=textIsLatin1||!strings.append(r.front())){JSLinearString*linear=text->ensureLinear(cx);if(!linear)returnfalse;*match=StringMatch(linear,pat);returntrue;}if(!r.popFront())returnfalse;}}AutoCheckCannotGCnogc;if(text->hasLatin1Chars()){if(pat->hasLatin1Chars())*match=RopeMatchImpl<Latin1Char>(nogc,strings,pat->latin1Chars(nogc),patLen);else*match=RopeMatchImpl<Latin1Char>(nogc,strings,pat->twoByteChars(nogc),patLen);}else{if(pat->hasLatin1Chars())*match=RopeMatchImpl<char16_t>(nogc,strings,pat->latin1Chars(nogc),patLen);else*match=RopeMatchImpl<char16_t>(nogc,strings,pat->twoByteChars(nogc),patLen);}returntrue;}/* ES6 draft rc4 21.1.3.7. */booljs::str_includes(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);// Steps 1, 2, and 3RootedStringstr(cx,ToStringForStringFunction(cx,args.thisv()));if(!str)returnfalse;// Steps 4 and 5boolisRegExp;if(!IsRegExp(cx,args.get(0),&isRegExp))returnfalse;// Step 6if(isRegExp){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INVALID_ARG_TYPE,"first","","Regular Expression");returnfalse;}// Steps 7 and 8RootedLinearStringsearchStr(cx,ArgToRootedString(cx,args,0));if(!searchStr)returnfalse;// Steps 9 and 10uint32_tpos=0;if(args.hasDefined(1)){if(args[1].isInt32()){inti=args[1].toInt32();pos=(i<0)?0U:uint32_t(i);}else{doubled;if(!ToInteger(cx,args[1],&d))returnfalse;pos=uint32_t(Min(Max(d,0.0),double(UINT32_MAX)));}}// Step 11uint32_ttextLen=str->length();// Step 12uint32_tstart=Min(Max(pos,0U),textLen);// Steps 13 and 14JSLinearString*text=str->ensureLinear(cx);if(!text)returnfalse;args.rval().setBoolean(StringMatch(text,searchStr,start)!=-1);returntrue;}/* ES6 20120927 draft 15.5.4.7. */booljs::str_indexOf(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);// Steps 1, 2, and 3RootedStringstr(cx,ToStringForStringFunction(cx,args.thisv()));if(!str)returnfalse;// Steps 4 and 5RootedLinearStringsearchStr(cx,ArgToRootedString(cx,args,0));if(!searchStr)returnfalse;// Steps 6 and 7uint32_tpos=0;if(args.hasDefined(1)){if(args[1].isInt32()){inti=args[1].toInt32();pos=(i<0)?0U:uint32_t(i);}else{doubled;if(!ToInteger(cx,args[1],&d))returnfalse;pos=uint32_t(Min(Max(d,0.0),double(UINT32_MAX)));}}// Step 8uint32_ttextLen=str->length();// Step 9uint32_tstart=Min(Max(pos,0U),textLen);// Steps 10 and 11JSLinearString*text=str->ensureLinear(cx);if(!text)returnfalse;args.rval().setInt32(StringMatch(text,searchStr,start));returntrue;}template<typenameTextChar,typenamePatChar>staticint32_tLastIndexOfImpl(constTextChar*text,size_ttextLen,constPatChar*pat,size_tpatLen,size_tstart){MOZ_ASSERT(patLen>0);MOZ_ASSERT(patLen<=textLen);MOZ_ASSERT(start<=textLen-patLen);constPatCharp0=*pat;constPatChar*patNext=pat+1;constPatChar*patEnd=pat+patLen;for(constTextChar*t=text+start;t>=text;--t){if(*t==p0){constTextChar*t1=t+1;for(constPatChar*p1=patNext;p1<patEnd;++p1,++t1){if(*t1!=*p1)gotobreak_continue;}returnstatic_cast<int32_t>(t-text);}break_continue:;}return-1;}// ES2017 draft rev 6859bb9ccaea9c6ede81d71e5320e3833b92cb3e// 21.1.3.9 String.prototype.lastIndexOf ( searchString [ , position ] )booljs::str_lastIndexOf(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);// Steps 1-2.RootedStringstr(cx,ToStringForStringFunction(cx,args.thisv()));if(!str)returnfalse;// Step 3.RootedLinearStringsearchStr(cx,ArgToRootedString(cx,args,0));if(!searchStr)returnfalse;// Step 6.size_tlen=str->length();// Step 8.size_tsearchLen=searchStr->length();// Steps 4-5, 7.intstart=len-searchLen;// Start searching hereif(args.hasDefined(1)){if(args[1].isInt32()){inti=args[1].toInt32();if(i<=0)start=0;elseif(i<start)start=i;}else{doubled;if(!ToNumber(cx,args[1],&d))returnfalse;if(!IsNaN(d)){d=JS::ToInteger(d);if(d<=0)start=0;elseif(d<start)start=int(d);}}}if(searchLen>len){args.rval().setInt32(-1);returntrue;}if(searchLen==0){args.rval().setInt32(start);returntrue;}MOZ_ASSERT(0<=start&&size_t(start)<len);JSLinearString*text=str->ensureLinear(cx);if(!text)returnfalse;// Step 9.int32_tres;AutoCheckCannotGCnogc;if(text->hasLatin1Chars()){constLatin1Char*textChars=text->latin1Chars(nogc);if(searchStr->hasLatin1Chars())res=LastIndexOfImpl(textChars,len,searchStr->latin1Chars(nogc),searchLen,start);elseres=LastIndexOfImpl(textChars,len,searchStr->twoByteChars(nogc),searchLen,start);}else{constchar16_t*textChars=text->twoByteChars(nogc);if(searchStr->hasLatin1Chars())res=LastIndexOfImpl(textChars,len,searchStr->latin1Chars(nogc),searchLen,start);elseres=LastIndexOfImpl(textChars,len,searchStr->twoByteChars(nogc),searchLen,start);}args.rval().setInt32(res);returntrue;}booljs::HasSubstringAt(JSLinearString*text,JSLinearString*pat,size_tstart){MOZ_ASSERT(start+pat->length()<=text->length());size_tpatLen=pat->length();AutoCheckCannotGCnogc;if(text->hasLatin1Chars()){constLatin1Char*textChars=text->latin1Chars(nogc)+start;if(pat->hasLatin1Chars())returnPodEqual(textChars,pat->latin1Chars(nogc),patLen);returnEqualChars(textChars,pat->twoByteChars(nogc),patLen);}constchar16_t*textChars=text->twoByteChars(nogc)+start;if(pat->hasTwoByteChars())returnPodEqual(textChars,pat->twoByteChars(nogc),patLen);returnEqualChars(pat->latin1Chars(nogc),textChars,patLen);}/* ES6 draft rc3 21.1.3.18. */booljs::str_startsWith(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);// Steps 1, 2, and 3RootedStringstr(cx,ToStringForStringFunction(cx,args.thisv()));if(!str)returnfalse;// Steps 4 and 5boolisRegExp;if(!IsRegExp(cx,args.get(0),&isRegExp))returnfalse;// Step 6if(isRegExp){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INVALID_ARG_TYPE,"first","","Regular Expression");returnfalse;}// Steps 7 and 8RootedLinearStringsearchStr(cx,ArgToRootedString(cx,args,0));if(!searchStr)returnfalse;// Steps 9 and 10uint32_tpos=0;if(args.hasDefined(1)){if(args[1].isInt32()){inti=args[1].toInt32();pos=(i<0)?0U:uint32_t(i);}else{doubled;if(!ToInteger(cx,args[1],&d))returnfalse;pos=uint32_t(Min(Max(d,0.0),double(UINT32_MAX)));}}// Step 11uint32_ttextLen=str->length();// Step 12uint32_tstart=Min(Max(pos,0U),textLen);// Step 13uint32_tsearchLen=searchStr->length();// Step 14if(searchLen+start<searchLen||searchLen+start>textLen){args.rval().setBoolean(false);returntrue;}// Steps 15 and 16JSLinearString*text=str->ensureLinear(cx);if(!text)returnfalse;args.rval().setBoolean(HasSubstringAt(text,searchStr,start));returntrue;}/* ES6 draft rc3 21.1.3.6. */booljs::str_endsWith(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);// Steps 1, 2, and 3RootedStringstr(cx,ToStringForStringFunction(cx,args.thisv()));if(!str)returnfalse;// Steps 4 and 5boolisRegExp;if(!IsRegExp(cx,args.get(0),&isRegExp))returnfalse;// Step 6if(isRegExp){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_INVALID_ARG_TYPE,"first","","Regular Expression");returnfalse;}// Steps 7 and 8RootedLinearStringsearchStr(cx,ArgToRootedString(cx,args,0));if(!searchStr)returnfalse;// Step 9uint32_ttextLen=str->length();// Steps 10 and 11uint32_tpos=textLen;if(args.hasDefined(1)){if(args[1].isInt32()){inti=args[1].toInt32();pos=(i<0)?0U:uint32_t(i);}else{doubled;if(!ToInteger(cx,args[1],&d))returnfalse;pos=uint32_t(Min(Max(d,0.0),double(UINT32_MAX)));}}// Step 12uint32_tend=Min(Max(pos,0U),textLen);// Step 13uint32_tsearchLen=searchStr->length();// Step 15 (reordered)if(searchLen>end){args.rval().setBoolean(false);returntrue;}// Step 14uint32_tstart=end-searchLen;// Steps 16 and 17JSLinearString*text=str->ensureLinear(cx);if(!text)returnfalse;args.rval().setBoolean(HasSubstringAt(text,searchStr,start));returntrue;}template<typenameCharT>staticvoidTrimString(constCharT*chars,booltrimLeft,booltrimRight,size_tlength,size_t*pBegin,size_t*pEnd){size_tbegin=0,end=length;if(trimLeft){while(begin<length&&unicode::IsSpace(chars[begin]))++begin;}if(trimRight){while(end>begin&&unicode::IsSpace(chars[end-1]))--end;}*pBegin=begin;*pEnd=end;}staticboolTrimString(JSContext*cx,constCallArgs&args,booltrimLeft,booltrimRight){RootedStringstr(cx,ToStringForStringFunction(cx,args.thisv()));if(!str)returnfalse;JSLinearString*linear=str->ensureLinear(cx);if(!linear)returnfalse;size_tlength=linear->length();size_tbegin,end;if(linear->hasLatin1Chars()){AutoCheckCannotGCnogc;TrimString(linear->latin1Chars(nogc),trimLeft,trimRight,length,&begin,&end);}else{AutoCheckCannotGCnogc;TrimString(linear->twoByteChars(nogc),trimLeft,trimRight,length,&begin,&end);}str=NewDependentString(cx,str,begin,end-begin);if(!str)returnfalse;args.rval().setString(str);returntrue;}booljs::str_trim(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);returnTrimString(cx,args,true,true);}booljs::str_trimLeft(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);returnTrimString(cx,args,true,false);}booljs::str_trimRight(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);returnTrimString(cx,args,false,true);}// Utility for building a rope (lazy concatenation) of strings.classRopeBuilder{JSContext*cx;RootedStringres;RopeBuilder(constRopeBuilder&other)=delete;voidoperator=(constRopeBuilder&other)=delete;public:explicitRopeBuilder(JSContext*cx):cx(cx),res(cx,cx->runtime()->emptyString){}inlineboolappend(HandleStringstr){res=ConcatStrings<CanGC>(cx,res,str);return!!res;}inlineJSString*result(){returnres;}};namespace{template<typenameCharT>staticuint32_tFindDollarIndex(constCharT*chars,size_tlength){if(constCharT*p=js_strchr_limit(chars,'$',chars+length)){uint32_tdollarIndex=p-chars;MOZ_ASSERT(dollarIndex<length);returndollarIndex;}returnUINT32_MAX;}}/* anonymous namespace */staticJSString*BuildFlatReplacement(JSContext*cx,HandleStringtextstr,HandleStringrepstr,size_tmatch,size_tpatternLength){RopeBuilderbuilder(cx);size_tmatchEnd=match+patternLength;if(textstr->isRope()){/* * If we are replacing over a rope, avoid flattening it by iterating * through it, building a new rope. */StringSegmentRanger(cx);if(!r.init(textstr))returnnullptr;size_tpos=0;while(!r.empty()){RootedStringstr(cx,r.front());size_tlen=str->length();size_tstrEnd=pos+len;if(pos<matchEnd&&strEnd>match){/* * We need to special-case any part of the rope that overlaps * with the replacement string. */if(match>=pos){/* * If this part of the rope overlaps with the left side of * the pattern, then it must be the only one to overlap with * the first character in the pattern, so we include the * replacement string here. */RootedStringleftSide(cx,NewDependentString(cx,str,0,match-pos));if(!leftSide||!builder.append(leftSide)||!builder.append(repstr)){returnnullptr;}}/* * If str runs off the end of the matched string, append the * last part of str. */if(strEnd>matchEnd){RootedStringrightSide(cx,NewDependentString(cx,str,matchEnd-pos,strEnd-matchEnd));if(!rightSide||!builder.append(rightSide))returnnullptr;}}else{if(!builder.append(str))returnnullptr;}pos+=str->length();if(!r.popFront())returnnullptr;}}else{RootedStringleftSide(cx,NewDependentString(cx,textstr,0,match));if(!leftSide)returnnullptr;RootedStringrightSide(cx);rightSide=NewDependentString(cx,textstr,match+patternLength,textstr->length()-match-patternLength);if(!rightSide||!builder.append(leftSide)||!builder.append(repstr)||!builder.append(rightSide)){returnnullptr;}}returnbuilder.result();}template<typenameCharT>staticboolAppendDollarReplacement(StringBuffer&newReplaceChars,size_tfirstDollarIndex,size_tmatchStart,size_tmatchLimit,JSLinearString*text,constCharT*repChars,size_trepLength){MOZ_ASSERT(firstDollarIndex<repLength);/* Move the pre-dollar chunk in bulk. */newReplaceChars.infallibleAppend(repChars,firstDollarIndex);/* Move the rest char-by-char, interpreting dollars as we encounter them. */constCharT*repLimit=repChars+repLength;for(constCharT*it=repChars+firstDollarIndex;it<repLimit;++it){if(*it!='$'||it==repLimit-1){if(!newReplaceChars.append(*it))returnfalse;continue;}switch(*(it+1)){case'$':/* Eat one of the dollars. */if(!newReplaceChars.append(*it))returnfalse;break;case'&':if(!newReplaceChars.appendSubstring(text,matchStart,matchLimit-matchStart))returnfalse;break;case'`':if(!newReplaceChars.appendSubstring(text,0,matchStart))returnfalse;break;case'\'':if(!newReplaceChars.appendSubstring(text,matchLimit,text->length()-matchLimit))returnfalse;break;default:/* The dollar we saw was not special (no matter what its mother told it). */if(!newReplaceChars.append(*it))returnfalse;continue;}++it;/* We always eat an extra char in the above switch. */}returntrue;}/* * Perform a linear-scan dollar substitution on the replacement text, * constructing a result string that looks like: * * newstring = string[:matchStart] + dollarSub(replaceValue) + string[matchLimit:] */staticJSString*BuildDollarReplacement(JSContext*cx,JSString*textstrArg,JSLinearString*repstr,uint32_tfirstDollarIndex,size_tmatchStart,size_tpatternLength){RootedLinearStringtextstr(cx,textstrArg->ensureLinear(cx));if(!textstr)returnnullptr;size_tmatchLimit=matchStart+patternLength;/* * Most probably: * * len(newstr) >= len(orig) - len(match) + len(replacement) * * Note that dollar vars _could_ make the resulting text smaller than this. */StringBuffernewReplaceChars(cx);if(repstr->hasTwoByteChars()&&!newReplaceChars.ensureTwoByteChars())returnnullptr;if(!newReplaceChars.reserve(textstr->length()-patternLength+repstr->length()))returnnullptr;boolres;if(repstr->hasLatin1Chars()){AutoCheckCannotGCnogc;res=AppendDollarReplacement(newReplaceChars,firstDollarIndex,matchStart,matchLimit,textstr,repstr->latin1Chars(nogc),repstr->length());}else{AutoCheckCannotGCnogc;res=AppendDollarReplacement(newReplaceChars,firstDollarIndex,matchStart,matchLimit,textstr,repstr->twoByteChars(nogc),repstr->length());}if(!res)returnnullptr;RootedStringleftSide(cx,NewDependentString(cx,textstr,0,matchStart));if(!leftSide)returnnullptr;RootedStringnewReplace(cx,newReplaceChars.finishString());if(!newReplace)returnnullptr;MOZ_ASSERT(textstr->length()>=matchLimit);RootedStringrightSide(cx,NewDependentString(cx,textstr,matchLimit,textstr->length()-matchLimit));if(!rightSide)returnnullptr;RopeBuilderbuilder(cx);if(!builder.append(leftSide)||!builder.append(newReplace)||!builder.append(rightSide))returnnullptr;returnbuilder.result();}template<typenameStrChar,typenameRepChar>staticboolStrFlatReplaceGlobal(JSContext*cx,JSLinearString*str,JSLinearString*pat,JSLinearString*rep,StringBuffer&sb){MOZ_ASSERT(str->length()>0);AutoCheckCannotGCnogc;constStrChar*strChars=str->chars<StrChar>(nogc);constRepChar*repChars=rep->chars<RepChar>(nogc);// The pattern is empty, so we interleave the replacement string in-between// each character.if(!pat->length()){CheckedInt<uint32_t>strLength(str->length());CheckedInt<uint32_t>repLength(rep->length());CheckedInt<uint32_t>length=repLength*(strLength-1)+strLength;if(!length.isValid()){ReportAllocationOverflow(cx);returnfalse;}if(!sb.reserve(length.value()))returnfalse;for(unsignedi=0;i<str->length()-1;++i,++strChars){sb.infallibleAppend(*strChars);sb.infallibleAppend(repChars,rep->length());}sb.infallibleAppend(*strChars);returntrue;}// If it's true, we are sure that the result's length is, at least, the same// length as |str->length()|.if(rep->length()>=pat->length()){if(!sb.reserve(str->length()))returnfalse;}uint32_tstart=0;for(;;){intmatch=StringMatch(str,pat,start);if(match<0)break;if(!sb.append(strChars+start,match-start))returnfalse;if(!sb.append(repChars,rep->length()))returnfalse;start=match+pat->length();}if(!sb.append(strChars+start,str->length()-start))returnfalse;returntrue;}// This is identical to "str.split(pattern).join(replacement)" except that we// do some deforestation optimization in Ion.JSString*js::str_flat_replace_string(JSContext*cx,HandleStringstring,HandleStringpattern,HandleStringreplacement){MOZ_ASSERT(string);MOZ_ASSERT(pattern);MOZ_ASSERT(replacement);if(!string->length())returnstring;RootedLinearStringlinearRepl(cx,replacement->ensureLinear(cx));if(!linearRepl)returnnullptr;RootedLinearStringlinearPat(cx,pattern->ensureLinear(cx));if(!linearPat)returnnullptr;RootedLinearStringlinearStr(cx,string->ensureLinear(cx));if(!linearStr)returnnullptr;StringBuffersb(cx);if(linearStr->hasTwoByteChars()){if(!sb.ensureTwoByteChars())returnnullptr;if(linearRepl->hasTwoByteChars()){if(!StrFlatReplaceGlobal<char16_t,char16_t>(cx,linearStr,linearPat,linearRepl,sb))returnnullptr;}else{if(!StrFlatReplaceGlobal<char16_t,Latin1Char>(cx,linearStr,linearPat,linearRepl,sb))returnnullptr;}}else{if(linearRepl->hasTwoByteChars()){if(!sb.ensureTwoByteChars())returnnullptr;if(!StrFlatReplaceGlobal<Latin1Char,char16_t>(cx,linearStr,linearPat,linearRepl,sb))returnnullptr;}else{if(!StrFlatReplaceGlobal<Latin1Char,Latin1Char>(cx,linearStr,linearPat,linearRepl,sb))returnnullptr;}}JSString*str=sb.finishString();if(!str)returnnullptr;returnstr;}JSString*js::str_replace_string_raw(JSContext*cx,HandleStringstring,HandleStringpattern,HandleStringreplacement){RootedLinearStringrepl(cx,replacement->ensureLinear(cx));if(!repl)returnnullptr;RootedAtompat(cx,AtomizeString(cx,pattern));if(!pat)returnnullptr;size_tpatternLength=pat->length();int32_tmatch;uint32_tdollarIndex;{AutoCheckCannotGCnogc;dollarIndex=repl->hasLatin1Chars()?FindDollarIndex(repl->latin1Chars(nogc),repl->length()):FindDollarIndex(repl->twoByteChars(nogc),repl->length());}/* * |string| could be a rope, so we want to avoid flattening it for as * long as possible. */if(string->isRope()){if(!RopeMatch(cx,&string->asRope(),pat,&match))returnnullptr;}else{match=StringMatch(&string->asLinear(),pat,0);}if(match<0)returnstring;if(dollarIndex!=UINT32_MAX)returnBuildDollarReplacement(cx,string,repl,dollarIndex,match,patternLength);returnBuildFlatReplacement(cx,string,repl,match,patternLength);}// ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.staticJSObject*SplitHelper(JSContext*cx,HandleLinearStringstr,uint32_tlimit,HandleLinearStringsep,HandleObjectGroupgroup){size_tstrLength=str->length();size_tsepLength=sep->length();MOZ_ASSERT(sepLength!=0);// Step 12.if(strLength==0){// Step 12.a.intmatch=StringMatch(str,sep,0);// Step 12.b.if(match!=-1)returnNewFullyAllocatedArrayTryUseGroup(cx,group,0);// Steps 12.c-e.RootedValuev(cx,StringValue(str));returnNewCopiedArrayTryUseGroup(cx,group,v.address(),1);}// Step 3 (reordered).AutoValueVectorsplits(cx);// Step 8 (reordered).size_tlastEndIndex=0;// Step 13.size_tindex=0;// Step 14.while(index!=strLength){// Step 14.a.intmatch=StringMatch(str,sep,index);// Step 14.b.//// Our match algorithm differs from the spec in that it returns the// next index at which a match happens. If no match happens we're// done.//// But what if the match is at the end of the string (and the string is// not empty)? Per 14.c.i this shouldn't be a match, so we have to// specially exclude it. Thus this case should hold://// var a = "abc".split(/\b/);// assertEq(a.length, 1);// assertEq(a[0], "abc");if(match==-1)break;// Step 14.c.size_tendIndex=match+sepLength;// Step 14.c.i.if(endIndex==lastEndIndex){index++;continue;}// Step 14.c.ii.MOZ_ASSERT(lastEndIndex<endIndex);MOZ_ASSERT(sepLength<=strLength);MOZ_ASSERT(lastEndIndex+sepLength<=endIndex);// Step 14.c.ii.1.size_tsubLength=size_t(endIndex-sepLength-lastEndIndex);JSString*sub=NewDependentString(cx,str,lastEndIndex,subLength);// Steps 14.c.ii.2-4.if(!sub||!splits.append(StringValue(sub)))returnnullptr;// Step 14.c.ii.5.if(splits.length()==limit)returnNewCopiedArrayTryUseGroup(cx,group,splits.begin(),splits.length());// Step 14.c.ii.6.index=endIndex;// Step 14.c.ii.7.lastEndIndex=index;}// Step 15.JSString*sub=NewDependentString(cx,str,lastEndIndex,strLength-lastEndIndex);// Steps 16-17.if(!sub||!splits.append(StringValue(sub)))returnnullptr;// Step 18.returnNewCopiedArrayTryUseGroup(cx,group,splits.begin(),splits.length());}// Fast-path for splitting a string into a character array via split("").staticJSObject*CharSplitHelper(JSContext*cx,HandleLinearStringstr,uint32_tlimit,HandleObjectGroupgroup){size_tstrLength=str->length();if(strLength==0)returnNewFullyAllocatedArrayTryUseGroup(cx,group,0);js::StaticStrings&staticStrings=cx->staticStrings();uint32_tresultlen=(limit<strLength?limit:strLength);AutoValueVectorsplits(cx);if(!splits.reserve(resultlen))returnnullptr;for(size_ti=0;i<resultlen;++i){JSString*sub=staticStrings.getUnitStringForElement(cx,str,i);if(!sub)returnnullptr;splits.infallibleAppend(StringValue(sub));}returnNewCopiedArrayTryUseGroup(cx,group,splits.begin(),splits.length());}template<typenameTextChar>staticMOZ_ALWAYS_INLINEJSObject*SplitSingleCharHelper(JSContext*cx,HandleLinearStringstr,constTextChar*text,uint32_ttextLen,char16_tpatCh,HandleObjectGroupgroup){// Count the number of occurrences of patCh within text.uint32_tcount=0;for(size_tindex=0;index<textLen;index++){if(static_cast<char16_t>(text[index])==patCh)count++;}// Handle zero-occurrence case - return input string in an array.if(count==0){RootedValuestrValue(cx,StringValue(str.get()));returnNewCopiedArrayTryUseGroup(cx,group,&strValue.get(),1);}// Reserve memory for substring values.AutoValueVectorsplits(cx);if(!splits.reserve(count+1))returnnullptr;// Add substrings.size_tlastEndIndex=0;for(size_tindex=0;index<textLen;index++){if(static_cast<char16_t>(text[index])==patCh){size_tsubLength=size_t(index-lastEndIndex);JSString*sub=NewDependentString(cx,str,lastEndIndex,subLength);if(!sub||!splits.append(StringValue(sub)))returnnullptr;lastEndIndex=index+1;}}// Add substring for tail of string (after last match).JSString*sub=NewDependentString(cx,str,lastEndIndex,textLen-lastEndIndex);if(!sub||!splits.append(StringValue(sub)))returnnullptr;returnNewCopiedArrayTryUseGroup(cx,group,splits.begin(),splits.length());}// ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.staticJSObject*SplitSingleCharHelper(JSContext*cx,HandleLinearStringstr,char16_tch,HandleObjectGroupgroup){// Step 12.size_tstrLength=str->length();AutoStableStringCharslinearChars(cx);if(!linearChars.init(cx,str))returnnullptr;if(linearChars.isLatin1()){returnSplitSingleCharHelper(cx,str,linearChars.latin1Chars(),strLength,ch,group);}else{returnSplitSingleCharHelper(cx,str,linearChars.twoByteChars(),strLength,ch,group);}}// ES 2016 draft Mar 25, 2016 21.1.3.17 steps 4, 8, 12-18.JSObject*js::str_split_string(JSContext*cx,HandleObjectGroupgroup,HandleStringstr,HandleStringsep,uint32_tlimit){RootedLinearStringlinearStr(cx,str->ensureLinear(cx));if(!linearStr)returnnullptr;RootedLinearStringlinearSep(cx,sep->ensureLinear(cx));if(!linearSep)returnnullptr;if(linearSep->length()==0)returnCharSplitHelper(cx,linearStr,limit,group);if(linearSep->length()==1&&limit>=static_cast<uint32_t>(INT32_MAX)){char16_tch=linearSep->latin1OrTwoByteChar(0);returnSplitSingleCharHelper(cx,linearStr,ch,group);}returnSplitHelper(cx,linearStr,limit,linearSep,group);}/* * Python-esque sequence operations. */booljs::str_concat(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);JSString*str=ToStringForStringFunction(cx,args.thisv());if(!str)returnfalse;for(unsignedi=0;i<args.length();i++){JSString*argStr=ToString<NoGC>(cx,args[i]);if(!argStr){RootedStringstrRoot(cx,str);argStr=ToString<CanGC>(cx,args[i]);if(!argStr)returnfalse;str=strRoot;}JSString*next=ConcatStrings<NoGC>(cx,str,argStr);if(next){str=next;}else{RootedStringstrRoot(cx,str),argStrRoot(cx,argStr);str=ConcatStrings<CanGC>(cx,strRoot,argStrRoot);if(!str)returnfalse;}}args.rval().setString(str);returntrue;}staticconstJSFunctionSpecstring_methods[]={#if JS_HAS_TOSOURCEJS_FN(js_toSource_str,str_toSource,0,0),#endif/* Java-like methods. */JS_FN(js_toString_str,str_toString,0,0),JS_FN(js_valueOf_str,str_toString,0,0),JS_FN("toLowerCase",str_toLowerCase,0,0),JS_FN("toUpperCase",str_toUpperCase,0,0),JS_INLINABLE_FN("charAt",str_charAt,1,0,StringCharAt),JS_INLINABLE_FN("charCodeAt",str_charCodeAt,1,0,StringCharCodeAt),JS_SELF_HOSTED_FN("substring","String_substring",2,0),JS_SELF_HOSTED_FN("padStart","String_pad_start",2,0),JS_SELF_HOSTED_FN("padEnd","String_pad_end",2,0),JS_SELF_HOSTED_FN("codePointAt","String_codePointAt",1,0),JS_FN("includes",str_includes,1,0),JS_FN("indexOf",str_indexOf,1,0),JS_FN("lastIndexOf",str_lastIndexOf,1,0),JS_FN("startsWith",str_startsWith,1,0),JS_FN("endsWith",str_endsWith,1,0),JS_FN("trim",str_trim,0,0),JS_FN("trimLeft",str_trimLeft,0,0),JS_FN("trimRight",str_trimRight,0,0),#if EXPOSE_INTL_APIJS_SELF_HOSTED_FN("toLocaleLowerCase","String_toLocaleLowerCase",0,0),JS_SELF_HOSTED_FN("toLocaleUpperCase","String_toLocaleUpperCase",0,0),JS_SELF_HOSTED_FN("localeCompare","String_localeCompare",1,0),#elseJS_FN("toLocaleLowerCase",str_toLocaleLowerCase,0,0),JS_FN("toLocaleUpperCase",str_toLocaleUpperCase,0,0),JS_FN("localeCompare",str_localeCompare,1,0),#endifJS_SELF_HOSTED_FN("repeat","String_repeat",1,0),#if EXPOSE_INTL_APIJS_FN("normalize",str_normalize,0,0),#endif/* Perl-ish methods (search is actually Python-esque). */JS_SELF_HOSTED_FN("match","String_match",1,0),JS_SELF_HOSTED_FN("search","String_search",1,0),JS_SELF_HOSTED_FN("replace","String_replace",2,0),JS_SELF_HOSTED_FN("split","String_split",2,0),JS_SELF_HOSTED_FN("substr","String_substr",2,0),/* Python-esque sequence methods. */JS_FN("concat",str_concat,1,0),JS_SELF_HOSTED_FN("slice","String_slice",2,0),/* HTML string methods. */JS_SELF_HOSTED_FN("bold","String_bold",0,0),JS_SELF_HOSTED_FN("italics","String_italics",0,0),JS_SELF_HOSTED_FN("fixed","String_fixed",0,0),JS_SELF_HOSTED_FN("strike","String_strike",0,0),JS_SELF_HOSTED_FN("small","String_small",0,0),JS_SELF_HOSTED_FN("big","String_big",0,0),JS_SELF_HOSTED_FN("blink","String_blink",0,0),JS_SELF_HOSTED_FN("sup","String_sup",0,0),JS_SELF_HOSTED_FN("sub","String_sub",0,0),JS_SELF_HOSTED_FN("anchor","String_anchor",1,0),JS_SELF_HOSTED_FN("link","String_link",1,0),JS_SELF_HOSTED_FN("fontcolor","String_fontcolor",1,0),JS_SELF_HOSTED_FN("fontsize","String_fontsize",1,0),JS_SELF_HOSTED_SYM_FN(iterator,"String_iterator",0,0),JS_FS_END};// ES6 rev 27 (2014 Aug 24) 21.1.1booljs::StringConstructor(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedStringstr(cx);if(args.length()>0){if(!args.isConstructing()&&args[0].isSymbol())returnjs::SymbolDescriptiveString(cx,args[0].toSymbol(),args.rval());str=ToString<CanGC>(cx,args[0]);if(!str)returnfalse;}else{str=cx->runtime()->emptyString;}if(args.isConstructing()){RootedObjectproto(cx);RootedObjectnewTarget(cx,&args.newTarget().toObject());if(!GetPrototypeFromConstructor(cx,newTarget,&proto))returnfalse;StringObject*strobj=StringObject::create(cx,str,proto);if(!strobj)returnfalse;args.rval().setObject(*strobj);returntrue;}args.rval().setString(str);returntrue;}staticboolstr_fromCharCode_few_args(JSContext*cx,constCallArgs&args){MOZ_ASSERT(args.length()<=JSFatInlineString::MAX_LENGTH_TWO_BYTE);char16_tchars[JSFatInlineString::MAX_LENGTH_TWO_BYTE];for(unsignedi=0;i<args.length();i++){uint16_tcode;if(!ToUint16(cx,args[i],&code))returnfalse;chars[i]=char16_t(code);}JSString*str=NewStringCopyN<CanGC>(cx,chars,args.length());if(!str)returnfalse;args.rval().setString(str);returntrue;}booljs::str_fromCharCode(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);MOZ_ASSERT(args.length()<=ARGS_LENGTH_MAX);// Optimize the single-char case.if(args.length()==1)returnstr_fromCharCode_one_arg(cx,args[0],args.rval());// Optimize the case where the result will definitely fit in an inline// string (thin or fat) and so we don't need to malloc the chars. (We could// cover some cases where args.length() goes up to// JSFatInlineString::MAX_LENGTH_LATIN1 if we also checked if the chars are// all Latin-1, but it doesn't seem worth the effort.)if(args.length()<=JSFatInlineString::MAX_LENGTH_TWO_BYTE)returnstr_fromCharCode_few_args(cx,args);char16_t*chars=cx->pod_malloc<char16_t>(args.length()+1);if(!chars)returnfalse;for(unsignedi=0;i<args.length();i++){uint16_tcode;if(!ToUint16(cx,args[i],&code)){js_free(chars);returnfalse;}chars[i]=char16_t(code);}chars[args.length()]=0;JSString*str=NewString<CanGC>(cx,chars,args.length());if(!str){js_free(chars);returnfalse;}args.rval().setString(str);returntrue;}staticinlineboolCodeUnitToString(JSContext*cx,uint16_tucode,MutableHandleValuerval){if(StaticStrings::hasUnit(ucode)){rval.setString(cx->staticStrings().getUnit(ucode));returntrue;}char16_tc=char16_t(ucode);JSString*str=NewStringCopyN<CanGC>(cx,&c,1);if(!str)returnfalse;rval.setString(str);returntrue;}booljs::str_fromCharCode_one_arg(JSContext*cx,HandleValuecode,MutableHandleValuerval){uint16_tucode;if(!ToUint16(cx,code,&ucode))returnfalse;returnCodeUnitToString(cx,ucode,rval);}staticMOZ_ALWAYS_INLINEboolToCodePoint(JSContext*cx,HandleValuecode,uint32_t*codePoint){// String.fromCodePoint, Steps 5.a-b.doublenextCP;if(!ToNumber(cx,code,&nextCP))returnfalse;// String.fromCodePoint, Steps 5.c-d.if(JS::ToInteger(nextCP)!=nextCP||nextCP<0||nextCP>unicode::NonBMPMax){ToCStringBufcbuf;if(char*numStr=NumberToCString(cx,&cbuf,nextCP))JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_NOT_A_CODEPOINT,numStr);returnfalse;}*codePoint=uint32_t(nextCP);returntrue;}booljs::str_fromCodePoint_one_arg(JSContext*cx,HandleValuecode,MutableHandleValuerval){// Steps 1-4 (omitted).// Steps 5.a-d.uint32_tcodePoint;if(!ToCodePoint(cx,code,&codePoint))returnfalse;// Steps 5.e, 6.if(!unicode::IsSupplementary(codePoint))returnCodeUnitToString(cx,uint16_t(codePoint),rval);char16_tchars[]={unicode::LeadSurrogate(codePoint),unicode::TrailSurrogate(codePoint)};JSString*str=NewStringCopyNDontDeflate<CanGC>(cx,chars,2);if(!str)returnfalse;rval.setString(str);returntrue;}staticboolstr_fromCodePoint_few_args(JSContext*cx,constCallArgs&args){MOZ_ASSERT(args.length()<=JSFatInlineString::MAX_LENGTH_TWO_BYTE/2);// Steps 1-2 (omitted).// Step 3.char16_telements[JSFatInlineString::MAX_LENGTH_TWO_BYTE];// Steps 4-5.unsignedlength=0;for(unsignednextIndex=0;nextIndex<args.length();nextIndex++){// Steps 5.a-d.uint32_tcodePoint;if(!ToCodePoint(cx,args[nextIndex],&codePoint))returnfalse;// Step 5.e.unicode::UTF16Encode(codePoint,elements,&length);}// Step 6.JSString*str=NewStringCopyN<CanGC>(cx,elements,length);if(!str)returnfalse;args.rval().setString(str);returntrue;}// ES2017 draft rev 40edb3a95a475c1b251141ac681b8793129d9a6d// 21.1.2.2 String.fromCodePoint(...codePoints)booljs::str_fromCodePoint(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);// Optimize the single code-point case.if(args.length()==1)returnstr_fromCodePoint_one_arg(cx,args[0],args.rval());// Optimize the case where the result will definitely fit in an inline// string (thin or fat) and so we don't need to malloc the chars. (We could// cover some cases where |args.length()| goes up to// JSFatInlineString::MAX_LENGTH_LATIN1 / 2 if we also checked if the chars// are all Latin-1, but it doesn't seem worth the effort.)if(args.length()<=JSFatInlineString::MAX_LENGTH_TWO_BYTE/2)returnstr_fromCodePoint_few_args(cx,args);// Steps 1-2 (omitted).// Step 3.static_assert(ARGS_LENGTH_MAX<std::numeric_limits<size_t>::max()/2,"|args.length() * 2 + 1| does not overflow");char16_t*elements=cx->pod_malloc<char16_t>(args.length()*2+1);if(!elements)returnfalse;// Steps 4-5.unsignedlength=0;for(unsignednextIndex=0;nextIndex<args.length();nextIndex++){// Steps 5.a-d.uint32_tcodePoint;if(!ToCodePoint(cx,args[nextIndex],&codePoint)){js_free(elements);returnfalse;}// Step 5.e.unicode::UTF16Encode(codePoint,elements,&length);}elements[length]=0;// Step 6.JSString*str=NewString<CanGC>(cx,elements,length);if(!str){js_free(elements);returnfalse;}args.rval().setString(str);returntrue;}staticconstJSFunctionSpecstring_static_methods[]={JS_INLINABLE_FN("fromCharCode",js::str_fromCharCode,1,0,StringFromCharCode),JS_INLINABLE_FN("fromCodePoint",js::str_fromCodePoint,1,0,StringFromCodePoint),JS_SELF_HOSTED_FN("raw","String_static_raw",2,0),JS_SELF_HOSTED_FN("substring","String_static_substring",3,0),JS_SELF_HOSTED_FN("substr","String_static_substr",3,0),JS_SELF_HOSTED_FN("slice","String_static_slice",3,0),JS_SELF_HOSTED_FN("match","String_generic_match",2,0),JS_SELF_HOSTED_FN("replace","String_generic_replace",3,0),JS_SELF_HOSTED_FN("search","String_generic_search",2,0),JS_SELF_HOSTED_FN("split","String_generic_split",3,0),JS_SELF_HOSTED_FN("toLowerCase","String_static_toLowerCase",1,0),JS_SELF_HOSTED_FN("toUpperCase","String_static_toUpperCase",1,0),JS_SELF_HOSTED_FN("charAt","String_static_charAt",2,0),JS_SELF_HOSTED_FN("charCodeAt","String_static_charCodeAt",2,0),JS_SELF_HOSTED_FN("includes","String_static_includes",2,0),JS_SELF_HOSTED_FN("indexOf","String_static_indexOf",2,0),JS_SELF_HOSTED_FN("lastIndexOf","String_static_lastIndexOf",2,0),JS_SELF_HOSTED_FN("startsWith","String_static_startsWith",2,0),JS_SELF_HOSTED_FN("endsWith","String_static_endsWith",2,0),JS_SELF_HOSTED_FN("trim","String_static_trim",1,0),JS_SELF_HOSTED_FN("trimLeft","String_static_trimLeft",1,0),JS_SELF_HOSTED_FN("trimRight","String_static_trimRight",1,0),JS_SELF_HOSTED_FN("toLocaleLowerCase","String_static_toLocaleLowerCase",1,0),JS_SELF_HOSTED_FN("toLocaleUpperCase","String_static_toLocaleUpperCase",1,0),#if EXPOSE_INTL_APIJS_SELF_HOSTED_FN("normalize","String_static_normalize",1,0),#endifJS_SELF_HOSTED_FN("concat","String_static_concat",2,0),JS_SELF_HOSTED_FN("localeCompare","String_static_localeCompare",2,0),JS_FS_END};/* static */Shape*StringObject::assignInitialShape(JSContext*cx,Handle<StringObject*>obj){MOZ_ASSERT(obj->empty());returnNativeObject::addDataProperty(cx,obj,cx->names().length,LENGTH_SLOT,JSPROP_PERMANENT|JSPROP_READONLY);}JSObject*js::InitStringClass(JSContext*cx,HandleObjectobj){MOZ_ASSERT(obj->isNative());Handle<GlobalObject*>global=obj.as<GlobalObject>();Rooted<JSString*>empty(cx,cx->runtime()->emptyString);RootedObjectproto(cx,GlobalObject::createBlankPrototype(cx,global,&StringObject::class_));if(!proto)returnnullptr;Handle<StringObject*>protoObj=proto.as<StringObject>();if(!StringObject::init(cx,protoObj,empty))returnnullptr;/* Now create the String function. */RootedFunctionctor(cx);ctor=GlobalObject::createConstructor(cx,StringConstructor,cx->names().String,1,AllocKind::FUNCTION,&jit::JitInfo_String);if(!ctor)returnnullptr;if(!LinkConstructorAndPrototype(cx,ctor,proto))returnnullptr;if(!DefinePropertiesAndFunctions(cx,proto,nullptr,string_methods)||!DefinePropertiesAndFunctions(cx,ctor,nullptr,string_static_methods)){returnnullptr;}/* * Define escape/unescape, the URI encode/decode functions, and maybe * uneval on the global object. */if(!JS_DefineFunctions(cx,global,string_functions))returnnullptr;if(!GlobalObject::initBuiltinConstructor(cx,global,JSProto_String,ctor,proto))returnnullptr;returnproto;}constchar*js::ValueToPrintable(JSContext*cx,constValue&vArg,JSAutoByteString*bytes,boolasSource){RootedValuev(cx,vArg);JSString*str;if(asSource)str=ValueToSource(cx,v);elsestr=ToString<CanGC>(cx,v);if(!str)returnnullptr;str=QuoteString(cx,str,0);if(!str)returnnullptr;returnbytes->encodeLatin1(cx,str);}template<AllowGCallowGC>JSString*js::ToStringSlow(JSContext*cx,typenameMaybeRooted<Value,allowGC>::HandleTypearg){/* As with ToObjectSlow, callers must verify that |arg| isn't a string. */MOZ_ASSERT(!arg.isString());Valuev=arg;if(!v.isPrimitive()){MOZ_ASSERT(!cx->helperThread());if(!allowGC)returnnullptr;RootedValuev2(cx,v);if(!ToPrimitive(cx,JSTYPE_STRING,&v2))returnnullptr;v=v2;}JSString*str;if(v.isString()){str=v.toString();}elseif(v.isInt32()){str=Int32ToString<allowGC>(cx,v.toInt32());}elseif(v.isDouble()){str=NumberToString<allowGC>(cx,v.toDouble());}elseif(v.isBoolean()){str=BooleanToString(cx,v.toBoolean());}elseif(v.isNull()){str=cx->names().null;}elseif(v.isSymbol()){MOZ_ASSERT(!cx->helperThread());if(allowGC){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_SYMBOL_TO_STRING);}returnnullptr;}else{MOZ_ASSERT(v.isUndefined());str=cx->names().undefined;}returnstr;}templateJSString*js::ToStringSlow<CanGC>(JSContext*cx,HandleValuearg);templateJSString*js::ToStringSlow<NoGC>(JSContext*cx,constValue&arg);JS_PUBLIC_API(JSString*)js::ToStringSlow(JSContext*cx,HandleValuev){returnToStringSlow<CanGC>(cx,v);}staticJSString*SymbolToSource(JSContext*cx,Symbol*symbol){RootedStringdesc(cx,symbol->description());SymbolCodecode=symbol->code();if(code!=SymbolCode::InSymbolRegistry&&code!=SymbolCode::UniqueSymbol){// Well-known symbol.MOZ_ASSERT(uint32_t(code)<JS::WellKnownSymbolLimit);returndesc;}StringBufferbuf(cx);if(code==SymbolCode::InSymbolRegistry?!buf.append("Symbol.for("):!buf.append("Symbol("))returnnullptr;if(desc){desc=StringToSource(cx,desc);if(!desc||!buf.append(desc))returnnullptr;}if(!buf.append(')'))returnnullptr;returnbuf.finishString();}JSString*js::ValueToSource(JSContext*cx,HandleValuev){if(!CheckRecursionLimit(cx))returnnullptr;assertSameCompartment(cx,v);if(v.isUndefined())returncx->names().void0;if(v.isString())returnStringToSource(cx,v.toString());if(v.isSymbol())returnSymbolToSource(cx,v.toSymbol());if(v.isPrimitive()){/* Special case to preserve negative zero, _contra_ toString. */if(v.isDouble()&&IsNegativeZero(v.toDouble())){/* NB: _ucNstr rather than _ucstr to indicate non-terminated. */staticconstchar16_tjs_negzero_ucNstr[]={'-','0'};returnNewStringCopyN<CanGC>(cx,js_negzero_ucNstr,2);}returnToString<CanGC>(cx,v);}RootedValuefval(cx);RootedObjectobj(cx,&v.toObject());if(!GetProperty(cx,obj,obj,cx->names().toSource,&fval))returnnullptr;if(IsCallable(fval)){RootedValuev(cx);if(!js::Call(cx,fval,obj,&v))returnnullptr;returnToString<CanGC>(cx,v);}returnObjectToSource(cx,obj);}JSString*js::StringToSource(JSContext*cx,JSString*str){returnQuoteString(cx,str,'"');}booljs::EqualChars(JSLinearString*str1,JSLinearString*str2){MOZ_ASSERT(str1->length()==str2->length());size_tlen=str1->length();AutoCheckCannotGCnogc;if(str1->hasTwoByteChars()){if(str2->hasTwoByteChars())returnPodEqual(str1->twoByteChars(nogc),str2->twoByteChars(nogc),len);returnEqualChars(str2->latin1Chars(nogc),str1->twoByteChars(nogc),len);}if(str2->hasLatin1Chars())returnPodEqual(str1->latin1Chars(nogc),str2->latin1Chars(nogc),len);returnEqualChars(str1->latin1Chars(nogc),str2->twoByteChars(nogc),len);}booljs::EqualStrings(JSContext*cx,JSString*str1,JSString*str2,bool*result){if(str1==str2){*result=true;returntrue;}size_tlength1=str1->length();if(length1!=str2->length()){*result=false;returntrue;}JSLinearString*linear1=str1->ensureLinear(cx);if(!linear1)returnfalse;JSLinearString*linear2=str2->ensureLinear(cx);if(!linear2)returnfalse;*result=EqualChars(linear1,linear2);returntrue;}booljs::EqualStrings(JSLinearString*str1,JSLinearString*str2){if(str1==str2)returntrue;size_tlength1=str1->length();if(length1!=str2->length())returnfalse;returnEqualChars(str1,str2);}staticint32_tCompareStringsImpl(JSLinearString*str1,JSLinearString*str2){size_tlen1=str1->length();size_tlen2=str2->length();AutoCheckCannotGCnogc;if(str1->hasLatin1Chars()){constLatin1Char*chars1=str1->latin1Chars(nogc);returnstr2->hasLatin1Chars()?CompareChars(chars1,len1,str2->latin1Chars(nogc),len2):CompareChars(chars1,len1,str2->twoByteChars(nogc),len2);}constchar16_t*chars1=str1->twoByteChars(nogc);returnstr2->hasLatin1Chars()?CompareChars(chars1,len1,str2->latin1Chars(nogc),len2):CompareChars(chars1,len1,str2->twoByteChars(nogc),len2);}int32_tjs::CompareChars(constchar16_t*s1,size_tlen1,JSLinearString*s2){AutoCheckCannotGCnogc;returns2->hasLatin1Chars()?CompareChars(s1,len1,s2->latin1Chars(nogc),s2->length()):CompareChars(s1,len1,s2->twoByteChars(nogc),s2->length());}booljs::CompareStrings(JSContext*cx,JSString*str1,JSString*str2,int32_t*result){MOZ_ASSERT(str1);MOZ_ASSERT(str2);if(str1==str2){*result=0;returntrue;}JSLinearString*linear1=str1->ensureLinear(cx);if(!linear1)returnfalse;JSLinearString*linear2=str2->ensureLinear(cx);if(!linear2)returnfalse;*result=CompareStringsImpl(linear1,linear2);returntrue;}int32_tjs::CompareAtoms(JSAtom*atom1,JSAtom*atom2){returnCompareStringsImpl(atom1,atom2);}booljs::StringEqualsAscii(JSLinearString*str,constchar*asciiBytes){size_tlength=strlen(asciiBytes);#ifdef DEBUGfor(size_ti=0;i!=length;++i)MOZ_ASSERT(unsigned(asciiBytes[i])<=127);#endifif(length!=str->length())returnfalse;constLatin1Char*latin1=reinterpret_cast<constLatin1Char*>(asciiBytes);AutoCheckCannotGCnogc;returnstr->hasLatin1Chars()?PodEqual(latin1,str->latin1Chars(nogc),length):EqualChars(latin1,str->twoByteChars(nogc),length);}size_tjs_strlen(constchar16_t*s){constchar16_t*t;for(t=s;*t!=0;t++)continue;return(size_t)(t-s);}int32_tjs_strcmp(constchar16_t*lhs,constchar16_t*rhs){while(true){if(*lhs!=*rhs)returnint32_t(*lhs)-int32_t(*rhs);if(*lhs==0)return0;++lhs;++rhs;}}int32_tjs_fputs(constchar16_t*s,FILE*f){while(*s!=0){if(fputwc(wchar_t(*s),f)==WEOF)returnWEOF;s++;}return1;}UniqueCharsjs::DuplicateString(JSContext*cx,constchar*s){size_tn=strlen(s)+1;autoret=cx->make_pod_array<char>(n);if(!ret)returnret;PodCopy(ret.get(),s,n);returnret;}UniqueTwoByteCharsjs::DuplicateString(JSContext*cx,constchar16_t*s){size_tn=js_strlen(s)+1;autoret=cx->make_pod_array<char16_t>(n);if(!ret)returnret;PodCopy(ret.get(),s,n);returnret;}UniqueCharsjs::DuplicateString(constchar*s){returnUniqueChars(js_strdup(s));}UniqueCharsjs::DuplicateString(constchar*s,size_tn){UniqueCharsret(js_pod_malloc<char>(n+1));if(!ret)returnnullptr;PodCopy(ret.get(),s,n);ret[n]=0;returnret;}UniqueTwoByteCharsjs::DuplicateString(constchar16_t*s){returnDuplicateString(s,js_strlen(s));}UniqueTwoByteCharsjs::DuplicateString(constchar16_t*s,size_tn){UniqueTwoByteCharsret(js_pod_malloc<char16_t>(n+1));if(!ret)returnnullptr;PodCopy(ret.get(),s,n);ret[n]=0;returnret;}template<typenameCharT>constCharT*js_strchr_limit(constCharT*s,char16_tc,constCharT*limit){while(s<limit){if(*s==c)returns;s++;}returnnullptr;}templateconstLatin1Char*js_strchr_limit(constLatin1Char*s,char16_tc,constLatin1Char*limit);templateconstchar16_t*js_strchr_limit(constchar16_t*s,char16_tc,constchar16_t*limit);char16_t*js::InflateString(JSContext*cx,constchar*bytes,size_t*lengthp){size_tnchars;char16_t*chars;size_tnbytes=*lengthp;nchars=nbytes;chars=cx->pod_malloc<char16_t>(nchars+1);if(!chars)gotobad;for(size_ti=0;i<nchars;i++)chars[i]=(unsignedchar)bytes[i];*lengthp=nchars;chars[nchars]=0;returnchars;bad:// For compatibility with callers of JS_DecodeBytes we must zero lengthp// on errors.*lengthp=0;returnnullptr;}template<typenameCharT>booljs::DeflateStringToBuffer(JSContext*maybecx,constCharT*src,size_tsrclen,char*dst,size_t*dstlenp){size_tdstlen=*dstlenp;if(srclen>dstlen){for(size_ti=0;i<dstlen;i++)dst[i]=char(src[i]);if(maybecx){AutoSuppressGCsuppress(maybecx);JS_ReportErrorNumberASCII(maybecx,GetErrorMessage,nullptr,JSMSG_BUFFER_TOO_SMALL);}returnfalse;}for(size_ti=0;i<srclen;i++)dst[i]=char(src[i]);*dstlenp=srclen;returntrue;}templatebooljs::DeflateStringToBuffer(JSContext*maybecx,constLatin1Char*src,size_tsrclen,char*dst,size_t*dstlenp);templatebooljs::DeflateStringToBuffer(JSContext*maybecx,constchar16_t*src,size_tsrclen,char*dst,size_t*dstlenp);#define ____ false/* * Identifier start chars: * - 36: $ * - 65..90: A..Z * - 95: _ * - 97..122: a..z */constbooljs_isidstart[]={/* 0 1 2 3 4 5 6 7 8 9 *//* 0 */____,____,____,____,____,____,____,____,____,____,/* 1 */____,____,____,____,____,____,____,____,____,____,/* 2 */____,____,____,____,____,____,____,____,____,____,/* 3 */____,____,____,____,____,____,true,____,____,____,/* 4 */____,____,____,____,____,____,____,____,____,____,/* 5 */____,____,____,____,____,____,____,____,____,____,/* 6 */____,____,____,____,____,true,true,true,true,true,/* 7 */true,true,true,true,true,true,true,true,true,true,/* 8 */true,true,true,true,true,true,true,true,true,true,/* 9 */true,____,____,____,____,true,____,true,true,true,/* 10 */true,true,true,true,true,true,true,true,true,true,/* 11 */true,true,true,true,true,true,true,true,true,true,/* 12 */true,true,true,____,____,____,____,____};/* * Identifier chars: * - 36: $ * - 48..57: 0..9 * - 65..90: A..Z * - 95: _ * - 97..122: a..z */constbooljs_isident[]={/* 0 1 2 3 4 5 6 7 8 9 *//* 0 */____,____,____,____,____,____,____,____,____,____,/* 1 */____,____,____,____,____,____,____,____,____,____,/* 2 */____,____,____,____,____,____,____,____,____,____,/* 3 */____,____,____,____,____,____,true,____,____,____,/* 4 */____,____,____,____,____,____,____,____,true,true,/* 5 */true,true,true,true,true,true,true,true,____,____,/* 6 */____,____,____,____,____,true,true,true,true,true,/* 7 */true,true,true,true,true,true,true,true,true,true,/* 8 */true,true,true,true,true,true,true,true,true,true,/* 9 */true,____,____,____,____,true,____,true,true,true,/* 10 */true,true,true,true,true,true,true,true,true,true,/* 11 */true,true,true,true,true,true,true,true,true,true,/* 12 */true,true,true,____,____,____,____,____};/* Whitespace chars: '\t', '\n', '\v', '\f', '\r', ' '. */constbooljs_isspace[]={/* 0 1 2 3 4 5 6 7 8 9 *//* 0 */____,____,____,____,____,____,____,____,____,true,/* 1 */true,true,true,true,____,____,____,____,____,____,/* 2 */____,____,____,____,____,____,____,____,____,____,/* 3 */____,____,true,____,____,____,____,____,____,____,/* 4 */____,____,____,____,____,____,____,____,____,____,/* 5 */____,____,____,____,____,____,____,____,____,____,/* 6 */____,____,____,____,____,____,____,____,____,____,/* 7 */____,____,____,____,____,____,____,____,____,____,/* 8 */____,____,____,____,____,____,____,____,____,____,/* 9 */____,____,____,____,____,____,____,____,____,____,/* 10 */____,____,____,____,____,____,____,____,____,____,/* 11 */____,____,____,____,____,____,____,____,____,____,/* 12 */____,____,____,____,____,____,____,____};/* * Uri reserved chars + #: * - 35: # * - 36: $ * - 38: & * - 43: + * - 44: , * - 47: / * - 58: : * - 59: ; * - 61: = * - 63: ? * - 64: @ */staticconstbooljs_isUriReservedPlusPound[]={/* 0 1 2 3 4 5 6 7 8 9 *//* 0 */____,____,____,____,____,____,____,____,____,____,/* 1 */____,____,____,____,____,____,____,____,____,____,/* 2 */____,____,____,____,____,____,____,____,____,____,/* 3 */____,____,____,____,____,true,true,____,true,____,/* 4 */____,____,____,true,true,____,____,true,____,____,/* 5 */____,____,____,____,____,____,____,____,true,true,/* 6 */____,true,____,true,true,____,____,____,____,____,/* 7 */____,____,____,____,____,____,____,____,____,____,/* 8 */____,____,____,____,____,____,____,____,____,____,/* 9 */____,____,____,____,____,____,____,____,____,____,/* 10 */____,____,____,____,____,____,____,____,____,____,/* 11 */____,____,____,____,____,____,____,____,____,____,/* 12 */____,____,____,____,____,____,____,____};/* * Uri unescaped chars: * - 33: ! * - 39: ' * - 40: ( * - 41: ) * - 42: * * - 45: - * - 46: . * - 48..57: 0-9 * - 65..90: A-Z * - 95: _ * - 97..122: a-z * - 126: ~ */staticconstbooljs_isUriUnescaped[]={/* 0 1 2 3 4 5 6 7 8 9 *//* 0 */____,____,____,____,____,____,____,____,____,____,/* 1 */____,____,____,____,____,____,____,____,____,____,/* 2 */____,____,____,____,____,____,____,____,____,____,/* 3 */____,____,____,true,____,____,____,____,____,true,/* 4 */true,true,true,____,____,true,true,____,true,true,/* 5 */true,true,true,true,true,true,true,true,____,____,/* 6 */____,____,____,____,____,true,true,true,true,true,/* 7 */true,true,true,true,true,true,true,true,true,true,/* 8 */true,true,true,true,true,true,true,true,true,true,/* 9 */true,____,____,____,____,true,____,true,true,true,/* 10 */true,true,true,true,true,true,true,true,true,true,/* 11 */true,true,true,true,true,true,true,true,true,true,/* 12 */true,true,true,____,____,____,true,____};#undef ____#define URI_CHUNK 64UstaticinlineboolTransferBufferToString(StringBuffer&sb,MutableHandleValuerval){JSString*str=sb.finishString();if(!str)returnfalse;rval.setString(str);returntrue;}/* * ECMA 3, 15.1.3 URI Handling Function Properties * * The following are implementations of the algorithms * given in the ECMA specification for the hidden functions * 'Encode' and 'Decode'. */enumEncodeResult{Encode_Failure,Encode_BadUri,Encode_Success};template<typenameCharT>staticEncodeResultEncode(StringBuffer&sb,constCharT*chars,size_tlength,constbool*unescapedSet,constbool*unescapedSet2){staticconstcharHexDigits[]="0123456789ABCDEF";/* NB: uppercase */char16_thexBuf[4];hexBuf[0]='%';hexBuf[3]=0;for(size_tk=0;k<length;k++){char16_tc=chars[k];if(c<128&&(unescapedSet[c]||(unescapedSet2&&unescapedSet2[c]))){if(!sb.append(c))returnEncode_Failure;}else{if(unicode::IsTrailSurrogate(c))returnEncode_BadUri;uint32_tv;if(!unicode::IsLeadSurrogate(c)){v=c;}else{k++;if(k==length)returnEncode_BadUri;char16_tc2=chars[k];if(!unicode::IsTrailSurrogate(c2))returnEncode_BadUri;v=unicode::UTF16Decode(c,c2);}uint8_tutf8buf[4];size_tL=OneUcs4ToUtf8Char(utf8buf,v);for(size_tj=0;j<L;j++){hexBuf[1]=HexDigits[utf8buf[j]>>4];hexBuf[2]=HexDigits[utf8buf[j]&0xf];if(!sb.append(hexBuf,3))returnEncode_Failure;}}}returnEncode_Success;}staticboolEncode(JSContext*cx,HandleLinearStringstr,constbool*unescapedSet,constbool*unescapedSet2,MutableHandleValuerval){size_tlength=str->length();if(length==0){rval.setString(cx->runtime()->emptyString);returntrue;}StringBuffersb(cx);if(!sb.reserve(length))returnfalse;EncodeResultres;if(str->hasLatin1Chars()){AutoCheckCannotGCnogc;res=Encode(sb,str->latin1Chars(nogc),str->length(),unescapedSet,unescapedSet2);}else{AutoCheckCannotGCnogc;res=Encode(sb,str->twoByteChars(nogc),str->length(),unescapedSet,unescapedSet2);}if(res==Encode_Failure)returnfalse;if(res==Encode_BadUri){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_BAD_URI);returnfalse;}MOZ_ASSERT(res==Encode_Success);returnTransferBufferToString(sb,rval);}enumDecodeResult{Decode_Failure,Decode_BadUri,Decode_Success};template<typenameCharT>staticDecodeResultDecode(StringBuffer&sb,constCharT*chars,size_tlength,constbool*reservedSet){for(size_tk=0;k<length;k++){char16_tc=chars[k];if(c=='%'){size_tstart=k;if((k+2)>=length)returnDecode_BadUri;if(!JS7_ISHEX(chars[k+1])||!JS7_ISHEX(chars[k+2]))returnDecode_BadUri;uint32_tB=JS7_UNHEX(chars[k+1])*16+JS7_UNHEX(chars[k+2]);k+=2;if(!(B&0x80)){c=char16_t(B);}else{intn=1;while(B&(0x80>>n))n++;if(n==1||n>4)returnDecode_BadUri;uint8_toctets[4];octets[0]=(uint8_t)B;if(k+3*(n-1)>=length)returnDecode_BadUri;for(intj=1;j<n;j++){k++;if(chars[k]!='%')returnDecode_BadUri;if(!JS7_ISHEX(chars[k+1])||!JS7_ISHEX(chars[k+2]))returnDecode_BadUri;B=JS7_UNHEX(chars[k+1])*16+JS7_UNHEX(chars[k+2]);if((B&0xC0)!=0x80)returnDecode_BadUri;k+=2;octets[j]=char(B);}uint32_tv=JS::Utf8ToOneUcs4Char(octets,n);if(v>=unicode::NonBMPMin){if(v>unicode::NonBMPMax)returnDecode_BadUri;char16_tH=unicode::LeadSurrogate(v);if(!sb.append(H))returnDecode_Failure;c=unicode::TrailSurrogate(v);}else{c=char16_t(v);}}if(c<128&&reservedSet&&reservedSet[c]){if(!sb.append(chars+start,k-start+1))returnDecode_Failure;}else{if(!sb.append(c))returnDecode_Failure;}}else{if(!sb.append(c))returnDecode_Failure;}}returnDecode_Success;}staticboolDecode(JSContext*cx,HandleLinearStringstr,constbool*reservedSet,MutableHandleValuerval){size_tlength=str->length();if(length==0){rval.setString(cx->runtime()->emptyString);returntrue;}StringBuffersb(cx);DecodeResultres;if(str->hasLatin1Chars()){AutoCheckCannotGCnogc;res=Decode(sb,str->latin1Chars(nogc),str->length(),reservedSet);}else{AutoCheckCannotGCnogc;res=Decode(sb,str->twoByteChars(nogc),str->length(),reservedSet);}if(res==Decode_Failure)returnfalse;if(res==Decode_BadUri){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_BAD_URI);returnfalse;}MOZ_ASSERT(res==Decode_Success);returnTransferBufferToString(sb,rval);}staticboolstr_decodeURI(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedLinearStringstr(cx,ArgToRootedString(cx,args,0));if(!str)returnfalse;returnDecode(cx,str,js_isUriReservedPlusPound,args.rval());}staticboolstr_decodeURI_Component(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedLinearStringstr(cx,ArgToRootedString(cx,args,0));if(!str)returnfalse;returnDecode(cx,str,nullptr,args.rval());}staticboolstr_encodeURI(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedLinearStringstr(cx,ArgToRootedString(cx,args,0));if(!str)returnfalse;returnEncode(cx,str,js_isUriUnescaped,js_isUriReservedPlusPound,args.rval());}staticboolstr_encodeURI_Component(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);RootedLinearStringstr(cx,ArgToRootedString(cx,args,0));if(!str)returnfalse;returnEncode(cx,str,js_isUriUnescaped,nullptr,args.rval());}booljs::EncodeURI(JSContext*cx,StringBuffer&sb,constchar*chars,size_tlength){EncodeResultresult=Encode(sb,chars,length,js_isUriUnescaped,js_isUriReservedPlusPound);if(result==EncodeResult::Encode_Failure)returnfalse;if(result==EncodeResult::Encode_BadUri){JS_ReportErrorNumberASCII(cx,GetErrorMessage,nullptr,JSMSG_BAD_URI);returnfalse;}returntrue;}/* * Convert one UCS-4 char and write it into a UTF-8 buffer, which must be at * least 4 bytes long. Return the number of UTF-8 bytes of data written. */uint32_tjs::OneUcs4ToUtf8Char(uint8_t*utf8Buffer,uint32_tucs4Char){MOZ_ASSERT(ucs4Char<=unicode::NonBMPMax);if(ucs4Char<0x80){utf8Buffer[0]=uint8_t(ucs4Char);return1;}uint32_ta=ucs4Char>>11;uint32_tutf8Length=2;while(a){a>>=5;utf8Length++;}MOZ_ASSERT(utf8Length<=4);uint32_ti=utf8Length;while(--i){utf8Buffer[i]=uint8_t((ucs4Char&0x3F)|0x80);ucs4Char>>=6;}utf8Buffer[0]=uint8_t(0x100-(1<<(8-utf8Length))+ucs4Char);returnutf8Length;}size_tjs::PutEscapedStringImpl(char*buffer,size_tbufferSize,GenericPrinter*out,JSLinearString*str,uint32_tquote){size_tlen=str->length();AutoCheckCannotGCnogc;returnstr->hasLatin1Chars()?PutEscapedStringImpl(buffer,bufferSize,out,str->latin1Chars(nogc),len,quote):PutEscapedStringImpl(buffer,bufferSize,out,str->twoByteChars(nogc),len,quote);}template<typenameCharT>size_tjs::PutEscapedStringImpl(char*buffer,size_tbufferSize,GenericPrinter*out,constCharT*chars,size_tlength,uint32_tquote){enum{STOP,FIRST_QUOTE,LAST_QUOTE,CHARS,ESCAPE_START,ESCAPE_MORE}state;MOZ_ASSERT(quote==0||quote=='\''||quote=='"');MOZ_ASSERT_IF(!buffer,bufferSize==0);MOZ_ASSERT_IF(out,!buffer);if(bufferSize==0)buffer=nullptr;elsebufferSize--;constCharT*charsEnd=chars+length;size_tn=0;state=FIRST_QUOTE;unsignedshift=0;unsignedhex=0;unsignedu=0;charc=0;/* to quell GCC warnings */for(;;){switch(state){caseSTOP:gotostop;caseFIRST_QUOTE:state=CHARS;gotodo_quote;caseLAST_QUOTE:state=STOP;do_quote:if(quote==0)continue;c=(char)quote;break;caseCHARS:if(chars==charsEnd){state=LAST_QUOTE;continue;}u=*chars++;if(u<' '){if(u!=0){constchar*escape=strchr(js_EscapeMap,(int)u);if(escape){u=escape[1];gotodo_escape;}}gotodo_hex_escape;}if(u<127){if(u==quote||u=='\\')gotodo_escape;c=(char)u;}elseif(u<0x100){gotodo_hex_escape;}else{shift=16;hex=u;u='u';gotodo_escape;}break;do_hex_escape:shift=8;hex=u;u='x';do_escape:c='\\';state=ESCAPE_START;break;caseESCAPE_START:MOZ_ASSERT(' '<=u&&u<127);c=(char)u;state=ESCAPE_MORE;break;caseESCAPE_MORE:if(shift==0){state=CHARS;continue;}shift-=4;u=0xF&(hex>>shift);c=(char)(u+(u<10?'0':'A'-10));break;}if(buffer){MOZ_ASSERT(n<=bufferSize);if(n!=bufferSize){buffer[n]=c;}else{buffer[n]='\0';buffer=nullptr;}}elseif(out){if(!out->put(&c,1))returnsize_t(-1);}n++;}stop:if(buffer)buffer[n]='\0';returnn;}templatesize_tjs::PutEscapedStringImpl(char*buffer,size_tbufferSize,GenericPrinter*out,constLatin1Char*chars,size_tlength,uint32_tquote);templatesize_tjs::PutEscapedStringImpl(char*buffer,size_tbufferSize,GenericPrinter*out,constchar*chars,size_tlength,uint32_tquote);templatesize_tjs::PutEscapedStringImpl(char*buffer,size_tbufferSize,GenericPrinter*out,constchar16_t*chars,size_tlength,uint32_tquote);templatesize_tjs::PutEscapedString(char*buffer,size_tbufferSize,constLatin1Char*chars,size_tlength,uint32_tquote);templatesize_tjs::PutEscapedString(char*buffer,size_tbufferSize,constchar16_t*chars,size_tlength,uint32_tquote);staticboolFlatStringMatchHelper(JSContext*cx,HandleStringstr,HandleStringpattern,bool*isFlat,int32_t*match){RootedLinearStringlinearPattern(cx,pattern->ensureLinear(cx));if(!linearPattern)returnfalse;staticconstsize_tMAX_FLAT_PAT_LEN=256;if(linearPattern->length()>MAX_FLAT_PAT_LEN||StringHasRegExpMetaChars(linearPattern)){*isFlat=false;returntrue;}*isFlat=true;if(str->isRope()){if(!RopeMatch(cx,&str->asRope(),linearPattern,match))returnfalse;}else{*match=StringMatch(&str->asLinear(),linearPattern);}returntrue;}staticboolBuildFlatMatchArray(JSContext*cx,HandleStringstr,HandleStringpattern,int32_tmatch,MutableHandleValuerval){if(match<0){rval.setNull();returntrue;}/* Get the templateObject that defines the shape and type of the output object */JSObject*templateObject=cx->compartment()->regExps.getOrCreateMatchResultTemplateObject(cx);if(!templateObject)returnfalse;RootedArrayObjectarr(cx,NewDenseFullyAllocatedArrayWithTemplate(cx,1,templateObject));if(!arr)returnfalse;/* Store a Value for each pair. */arr->setDenseInitializedLength(1);arr->initDenseElement(0,StringValue(pattern));/* Set the |index| property. (TemplateObject positions it in slot 0) */arr->setSlot(0,Int32Value(match));/* Set the |input| property. (TemplateObject positions it in slot 1) */arr->setSlot(1,StringValue(str));#ifdef DEBUGRootedValuetest(cx);RootedIdid(cx,NameToId(cx->names().index));if(!NativeGetProperty(cx,arr,id,&test))returnfalse;MOZ_ASSERT(test==arr->getSlot(0));id=NameToId(cx->names().input);if(!NativeGetProperty(cx,arr,id,&test))returnfalse;MOZ_ASSERT(test==arr->getSlot(1));#endifrval.setObject(*arr);returntrue;}#ifdef DEBUGstaticboolCallIsStringOptimizable(JSContext*cx,constchar*name,bool*result){JSAtom*atom=Atomize(cx,name,strlen(name));if(!atom)returnfalse;RootedPropertyNamepropName(cx,atom->asPropertyName());RootedValuefuncVal(cx);if(!GlobalObject::getSelfHostedFunction(cx,cx->global(),propName,propName,0,&funcVal))returnfalse;FixedInvokeArgs<0>args(cx);RootedValuerval(cx);if(!Call(cx,funcVal,UndefinedHandleValue,args,&rval))returnfalse;*result=rval.toBoolean();returntrue;}#endifbooljs::FlatStringMatch(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);MOZ_ASSERT(args.length()==2);MOZ_ASSERT(args[0].isString());MOZ_ASSERT(args[1].isString());#ifdef DEBUGboolisOptimizable=false;if(!CallIsStringOptimizable(cx,"IsStringMatchOptimizable",&isOptimizable))returnfalse;MOZ_ASSERT(isOptimizable);#endifRootedStringstr(cx,args[0].toString());RootedStringpattern(cx,args[1].toString());boolisFlat=false;int32_tmatch=0;if(!FlatStringMatchHelper(cx,str,pattern,&isFlat,&match))returnfalse;if(!isFlat){args.rval().setUndefined();returntrue;}returnBuildFlatMatchArray(cx,str,pattern,match,args.rval());}booljs::FlatStringSearch(JSContext*cx,unsignedargc,Value*vp){CallArgsargs=CallArgsFromVp(argc,vp);MOZ_ASSERT(args.length()==2);MOZ_ASSERT(args[0].isString());MOZ_ASSERT(args[1].isString());#ifdef DEBUGboolisOptimizable=false;if(!CallIsStringOptimizable(cx,"IsStringSearchOptimizable",&isOptimizable))returnfalse;MOZ_ASSERT(isOptimizable);#endifRootedStringstr(cx,args[0].toString());RootedStringpattern(cx,args[1].toString());boolisFlat=false;int32_tmatch=0;if(!FlatStringMatchHelper(cx,str,pattern,&isFlat,&match))returnfalse;if(!isFlat){args.rval().setInt32(-2);returntrue;}args.rval().setInt32(match);returntrue;}